【问题标题】:Animated react routes not working with <Switch> component动画反应路线不适用于 <Switch> 组件
【发布时间】:2020-12-03 21:44:52
【问题描述】:

我正在尝试为我的 React 应用程序构建一个简单的路由器,它由从对象数组生成的一些路由和一个静态 404 路由组成。要求是每条路线之间的过渡都必须是动画的。

我在浏览器中使用react-router,在浏览器中使用react-transition-group

我想实现这样的目标(精简的、不完整的伪代码):

const routes = [
    { path: "/", component: Home },
    { path: "/about", component: About },
    { path: "/contact", component: Contact }
];

const createRoute = (route) => {
    return (
        <CSSTransition className="page" timeout={300}>
            <Route path={route.path} exact component={route.component} />
        </CSSTransition>
    );
}

<Router>
    {routes.map(createRoute)}
    <CSSTransition className="page" timeout={300}>
        <Route component={PageNotFound} />
    </CSSTransition>
</Router>

此代码的完整版本可以在此 Codesandbox 上找到:

https://codesandbox.io/s/react-router-switch-csstransition-catch-all-route-bug-forked-qzt9g

我需要使用 &lt;Switch&gt; 组件来防止 404 路由显示在应用程序的所有其他路由中,但是一旦我添加了 &lt;Switch&gt;,动画就会停止工作。

在上面的示例中,尽管遵循了react-routerreact-transition-group 的官方文档中的指南,但您将看到动画在与&lt;Switch&gt; 一起使用时不起作用。

但是,它们在不使用 &lt;Switch&gt; 的情况下完美运行,但当然我最终会一直显示 404 路线。

预期结果:

  • 所有路由之间的动画转换,动态创建的以及静态 404 页面

实际结果:

  • 根本没有动画或始终显示 404 路线的动画

我花了一整天的时间试图找到解决我遇到的问题的方法。不幸的是,我似乎找不到任何可以远程帮助我解决我面临的问题的东西,我已经在 Google、Stack Overflow、Medium 上进行了搜索,最后我回到了这里,希望有人能帮助我。

【问题讨论】:

    标签: reactjs react-router react-router-dom react-transition-group


    【解决方案1】:

    为了让动画与 Switch 组件一起工作,您必须将正确的 locationwithRouter 一起传递给 CSSTransition,再加上TransitionGroup 组件。 我已经使用以下工作解决方案修改了您的沙箱代码:

    import React from "react";
    import ReactDOM from "react-dom";
    import { CSSTransition, TransitionGroup } from "react-transition-group";
    import {
      BrowserRouter as Router,
      Route,
      Switch,
      Link,
      withRouter
    } from "react-router-dom";
    
    import "./styles.css";
    
    const rootElement = document.getElementById("root");
    
    const components = {
      Home: () => <div>Home page</div>,
      About: () => <div>About the company</div>,
      Contact: () => <div>Contact us</div>,
      NotFound: () => <div>404</div>,
      Menu: ({ links, setIsSwitch }) => (
        <div className="menu">
          {links.map(({ path, component }, key) => (
            <Link key={key} to={path}>
              {component}
            </Link>
          ))}
          <Link to="/404">404</Link>
        </div>
      )
    };
    
    const routes = [
      { path: "/", component: "Home" },
      { path: "/about", component: "About" },
      { path: "/contact", component: "Contact" }
    ];
    
    const createRoutes = (routes) =>
      routes.map(({ component, path }) => {
        const Component = components[component];
        const nodeRef = React.createRef();
    
        return (
          <Route key={path} path={path} exact>
            {({ match }) => {
              return (
                <div ref={nodeRef} className="page">
                  <Component />
                </div>
              );
            }}
          </Route>
        );
      });
    
    const AnimatedSwitch = withRouter(({ location }) => (
      <TransitionGroup>
        <CSSTransition
          key={location.key}
          timeout={500}
          classNames="page"
          unmountOnExit
        >
          <Switch location={location}>{createRoutes(routes)}</Switch>
        </CSSTransition>
      </TransitionGroup>
    ));
    
    ReactDOM.render(
      <React.StrictMode>
        <Router>
          <components.Menu links={routes} />
          <AnimatedSwitch />
        </Router>
      </React.StrictMode>,
      rootElement
    );
    

    本文详细解释了其背后的原因:https://latteandcode.medium.com/react-how-to-animate-transitions-between-react-router-routes-7f9cb7f5636a

    顺便说一下,withRouter 在 react-router v6 中已被弃用。 所以你应该自己实现那个钩子。

    import { useHistory } from 'react-router-dom';
    
    export const withRouter = (Component) => {
      const Wrapper = (props) => {
        const history = useHistory();
        
        return (
          <Component
            history={history}
            {...props}
            />
        );
      };
      
      return Wrapper;
    };
    

    请参阅 GitHub 上已弃用的问题讨论:https://github.com/remix-run/react-router/issues/7156

    【讨论】:

    • 谢谢@lamazing,它有效!赏金是你的 :)
    【解决方案2】:

    快速解决方法是您可以执行以下操作

    <Switch>
      <Route
        path={"/:page(|about|contact)"}
        render={() => createRoutes(routes)}
      />
      <Route>
        <div className="page">
          <components.NotFound />
        </div>
      </Route>
    </Switch>
    

    显然不是最优雅和可扩展的解决方案。但对于小型反应站点来说是可行的。

    这是工作代码的分叉代码框。 https://codesandbox.io/s/react-router-switch-csstransition-catch-all-route-bug-forked-dhv39

    【讨论】:

      【解决方案3】:

      编辑:我首先检查了router.location.key,但是通过地址栏到达会导致 404 页面呈现每个页面,所以这样更安全。

      您可以通过检查router.location.pathname将路由器作为道具传递给页面并有条件地呈现404页面

      export const routes = [
      ...all of your routes,
      {
          path: "*",
          Component: NotFound,
        },
      ]
      
      
          {routes.map(({ path, pageTitle, Component }) => (
              <Route key={path} exact path={path}>
                {router => (
                  <CSSTransition
                    in={router.match !== null}
                    timeout={400}
                    classNames="page"
                    unmountOnExit
                  >
                    <div className="page">
                      <Component router={router} />
                    </div>
                  </CSSTransition>
                )}
              </Route>
            ))}
      

      并且在您的 404 组件中,您需要检查当前路由的路径是否在所有路由周围都可用:

      import { routes } from "../Routes";
      
      const checkIfRouteIsValid = path => routes.findIndex(route => route.path === path);
      
      export default function NotFound(props) {
        const pathname = props.router.location.pathname;
        const [show, setShow] = useState(false);
        useEffect(() => {
         setShow(checkIfRouteIsValid(pathname) === -1);
        }, [pathname]);
          return (
            show && (
              <div>Not found!</div>
             )
           );
         }
      

      【讨论】:

        猜你喜欢
        • 2018-04-11
        • 2023-01-10
        • 1970-01-01
        • 2019-06-01
        • 2023-03-05
        • 2018-07-18
        • 2017-11-06
        • 1970-01-01
        • 2019-11-07
        相关资源
        最近更新 更多