使用 AnimatePresence (Framer Motion) 和 createBrowserRouter & RouterProvider (React Router DOM v6.4.1) 退出动画

问题描述 投票:0回答:2

React Router v6.4 引入了一个新的路由 API,带有 createBrowserRouterRouterProvider

在旧版本的 React Router 中,可以环绕使用 React Router 定义的路由来启用页面转换。当提供位置和关键点的值时,Framer Motion 可以检测是否在组件树中添加或删除了子组件以显示开始和退出过渡动画。

React Router v6.4 之前:

<AnimatePresence exitBeforeEnter>
  <Routes location={location} key={location.pathname}>
    <Route path="/" element={<HomePage />} />
  </Routes>
</AnimatePresence>

虽然页面加载时的动画仍然适用于新的路由 API,但我找不到让退出动画再次工作的方法。

反应路由器v6.4.1

...
const router = createBrowserRouter([
    {
      path: '/',
      element: (
         <HomePage />
      ),
    },
]);
...


<AnimatePresence mode="wait">
  <RouterProvider router={router} />
</AnimatePresence>

这里是一个使用旧版本 React Router 和 Framer Motion 的完整 React 应用程序的示例

javascript reactjs react-router-dom framer-motion
2个回答
11
投票

对于没有嵌套路由的应用程序,您可以使用 useOutlet 解决此问题。

您的 AnimatePresence 不应包装 RouterProvider,而应将其放置在包装其他路由的布局组件内。

在路由器声明中,创建一个顶级布局组件,例如 RootContainer。其余的路由可以使用 Children 属性声明,其中您的主页是索引路由:

const router = createBrowserRouter([
    {
      element: (
         <RootContainer />
      ),
      children: [
          {
             index: true,
             element: <HomePage />,
          },
          ...
      ]
    },
]);

RootContainer
里面你想要渲染一个Outlet,它是一个UI与当前路由匹配的组件。

要启用退出动画,我们需要使用“冻结”出口。这意味着每次位置发生变化时,插座参考保持不变。我们基本上将使用一个键来控制重新安装逻辑,类似于在 V5 实现中使用

Routes
完成的方式。有关该解决方案的更多上下文以及为什么此类功能不是react-router-dom核心的一部分:https://github.com/remix-run/react-router/discussions/8008#discussioncomment-1280897

AnimatedOutlet

const AnimatedOutlet: React.FC = () => {
    const o = useOutlet();
    const [outlet] = useState(o);

    return <>{outlet}</>;
};

在您的

RootContainer
中,用运动组件包裹 Outlet,并记住将当前路径名作为其键:

<AnimatePresence mode="popLayout">
     <motion.div
        key={location.pathname}
        initial={{ opacity: 0, x: 50 }}
        animate={{ opacity: 1, x: 0 }}
        exit={{ opacity: 0, x: 50 }}
     >
         <AnimatedOutlet />
     </motion.div>
</AnimatePresence>

不幸的是,对于具有许多嵌套路由的应用程序,此解决方案并不完全足够。在 V5 中,您可以通过使用位置“部分”来防止父路线在子路线导航期间重新渲染:

const location = useLocation()
const locationArr = location.pathname?.split('/') ?? [];

return 
   <Routes location={location} key={locationArr[1]}
     ...
        <Routes location={location} key={locationArr[2]}
           ...
        </Routes>
   </Routes>

我还不知道 useOutlet 是否可以实现这一点。


0
投票

扩展评论鲁特库拉

[Example Slide Routing V6][1]


[1]: https://codesandbox.io/p/sandbox/slidereactroutev6-7dlfvw?file=%2Fsrc%2Froutes.tsx%3A40%2C42
© www.soinside.com 2019 - 2024. All rights reserved.