Framer Motion与React Router DOM v6页面过渡致布局组件重渲染求助
解决Framer Motion + React Router DOM布局组件不必要重渲染的问题
问题核心是你给整个Routes组件加了key={location.pathname},导致路径变化时整个Routes重新挂载,连带里面的AdminLayout、Sidebar、Topbar全部重渲染。但直接去掉key又会导致退出过渡异常,下面是可行的修复方案:
方案一:将AnimatePresence下移到布局内部,只包裹页面内容
这是最根本的解决方式,让布局组件脱离AnimatePresence的影响,只给页面内容加动画:
- 修改Router代码,移除Routes上的key
<AnimatePresence mode="wait"> <Routes location={location}> {/* 删掉key={location.pathname} */} <Route element={<GuestMiddleware />}> // 无布局路由 </Route> <Route element={<AuthMiddleware />}> <Route element={<AdminLayout />}> // 带布局路由 </Route> </Route> </Routes> </AnimatePresence>
- 在AdminLayout中用AnimatePresence包裹Outlet
把动画逻辑限制在页面内容区域,布局组件(Sidebar、Topbar)不会再被重新挂载:
import { useLocation } from "react-router-dom"; import { AnimatePresence } from "framer-motion"; <> <Sidebar /> <div className={cn("transition-spacing flex min-h-d-screen w-full flex-col ps-0 duration-300", { "lg:ps-sidebar": isOpen })}> <Topbar /> <div className="grow"> <AnimatePresence mode="wait"> {/* 给Outlet渲染的页面加唯一key,确保AnimatePresence能识别页面切换 */} <Outlet key={useLocation().pathname} /> </AnimatePresence> </div> </div> </>
- 给每个页面组件添加motion动画
每个需要过渡的页面都用motion.div包裹,定义进入和退出动画:
import { motion } from "framer-motion"; const Dashboard = () => { return ( <motion.div initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} transition={{ duration: 0.3 }} > {/* 页面内容 */} </motion.div> ); };
方案二:用React.memo兜底优化布局组件
如果因为某些原因不能调整AnimatePresence的位置,可以用React.memo包裹Sidebar和Topbar,阻止它们因父组件重渲染而不必要更新:
import React from "react"; // 用React.memo包裹,只有props变化时才重渲染 const Sidebar = React.memo(() => { // Sidebar组件内容 }); const Topbar = React.memo(() => { // Topbar组件内容 });
注意:如果Sidebar/Topbar依赖的props(比如isOpen)变化,还是会正常重渲染,这是合理的。
内容的提问来源于stack exchange,提问作者alas.code
相关产品推荐
相关产品推荐

