React Router 6中如何阻止路由切换时组件卸载?
解决路由切换时组件卸载导致的内容异常问题
首先修正你的路由结构
你当前的路由配置存在嵌套错误:父路由/questions的element已经是<SharedLayoutQuestions />,但index子路由又重复渲染了一次该组件,这会导致嵌套渲染,且不符合布局路由的设计逻辑。正确的做法是让SharedLayoutQuestions作为布局容器,通过<Outlet />渲染子路由内容:
import { Routes, Route, Outlet } from 'react-router-dom'; // 修正后的路由结构 <Routes> <Route path='/' element={<Start />} /> <Route path='/questions' element={<SharedLayoutQuestions />}> <Route index element={<FirstQuestion />} /> {/* 替换为单独的首题组件 */} <Route path=':questionIndex' element={<SingleQuestion />} /> </Route> <Route path='*' element={<NotFound />} /> </Routes>
对应的SharedLayoutQuestions组件需要添加<Outlet />来承载子路由内容:
function SharedLayoutQuestions() { // 这里的状态会始终保留,因为父组件不会在/questions路径内切换时卸载 return ( <div className="questions-layout"> {/* 这里放共享的布局元素,比如导航、进度条等 */} <Outlet /> {/* 子路由的内容会渲染在这里 */} </div> ); }
这样修改后,SharedLayoutQuestions作为父布局组件,在/questions路径内切换路由时不会卸载,其内部状态会一直保留。
解决子组件(SingleQuestion/FirstQuestion)卸载导致的随机数问题
如果子组件在路由切换时会卸载,导致随机数重新生成,可通过以下两种方案解决:
方案1:提升随机数状态到父布局组件
将随机数的生成逻辑移到SharedLayoutQuestions中,用useRef保存值(只初始化一次),再通过路由上下文传递给子组件:
// SharedLayoutQuestions.jsx import { useRef, Outlet } from 'react'; function SharedLayoutQuestions() { // useRef确保随机数只在组件首次挂载时生成一次 const questionRandomRef = useRef(Math.random()); return ( <div className="questions-layout"> {/* 通过Outlet传递上下文 */} <Outlet context={{ randomValue: questionRandomRef.current }} /> </div> ); }
子组件中通过useOutletContext获取随机值:
// SingleQuestion.jsx import { useOutletContext } from 'react-router-dom'; function SingleQuestion() { const { randomValue } = useOutletContext(); // 这里使用randomValue,无论组件挂载/卸载多少次,值都不会变 return <div>当前随机值:{randomValue}</div>; }
方案2:缓存子组件阻止卸载
如果需要完全阻止子组件卸载,可以实现一个简易的KeepAlive组件来缓存已渲染的子组件:
// KeepAlive.jsx import { useState, useEffect } from 'react'; import { useLocation } from 'react-router-dom'; function KeepAlive({ children }) { const location = useLocation(); const [cachedComponents, setCachedComponents] = useState({}); const currentPath = location.pathname; useEffect(() => { // 缓存当前路径的组件 if (!cachedComponents[currentPath]) { setCachedComponents(prev => ({ ...prev, [currentPath]: children })); } }, [children, currentPath, cachedComponents]); // 渲染对应路径的缓存组件 return cachedComponents[currentPath] || null; }
在路由中使用该组件包裹子组件:
<Route path='/questions' element={<SharedLayoutQuestions />}> <Route index element={<KeepAlive><FirstQuestion /></KeepAlive>} /> <Route path=':questionIndex' element={<KeepAlive><SingleQuestion /></KeepAlive>} /> </Route>
注意:这种方式会缓存所有访问过的子组件,若需要清理缓存,可添加逻辑移除不再需要的路径缓存。
内容的提问来源于stack exchange,提问作者Aghas Ghazaryan
相关产品推荐
相关产品推荐

