首页 > 解决方案 > 动画反应路线不起作用零件

问题描述

我正在尝试为我的 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

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

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

但是,它们在不使用 的情况下完美地工作<Switch>,但是当然我最终会一直显示 404 路线。

预期结果:

实际结果:

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

标签: reactjsreact-routerreact-router-domreact-transition-group

解决方案


为了让动画与 Switch 组件一起使用,您必须使用withRouter将正确的位置传递给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 。

顺便说一句,在 react-router v6中不推荐使用withRouter。所以你应该自己实现那个钩子。

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 。


推荐阅读