使用React Transition Group嵌套路由时Context Provider卸载问题
问题描述
我开发了一个带嵌套路由的React应用,想用React Transition Group实现顶层路由和嵌套路由之间的转场效果,但遇到了问题:包裹嵌套路由的Context Provider会在嵌套路由转场时被卸载,导致状态丢失;但如果不使用转场功能,这个Provider就不会卸载,状态能正常保留。
示例代码
App.js
import React from "react"; import { Navigate, Route, Routes, NavLink, useLocation } from "react-router-dom"; import "./styles.css"; import { SwitchTransition, CSSTransition } from "react-transition-group"; const SubPage1 = () => { return ( <div> <h1>SubPage1</h1> <NavLink to="/2">Go to SubPage2</NavLink> </div> ); }; const SubPage2 = () => { return ( <div> <h1>SubPage2</h1> <NavLink to="/1">Go to SubPage1</NavLink> </div> ); }; const Page1 = () => { const location = useLocation(); return ( <MyContextProvider> <h1>Page1</h1> <NavLink to="/2">Go to Page2</NavLink> <Routes location={location}> <Route path="/1" element={<SubPage1 />} /> <Route path="/2" element={<SubPage2 />} /> <Route path="*" element={<Navigate to="/1" />} /> </Routes> </MyContextProvider> ); }; const Page2 = () => { return ( <div> <h1>Page2</h1> <NavLink to="/1">Go to Page1</NavLink> </div> ); }; const MyContext = React.createContext(); const MyContextProvider = ({ children }) => { React.useEffect(() => console.log("Context mounting"), []); return <MyContext.Provider>{children}</MyContext.Provider>; }; export default function App() { const location = useLocation(); return ( <SwitchTransition> <CSSTransition key={location.key} classNames="right-to-left" timeout={200} > <Routes> <Route path="/1" element={<Page1 />} /> <Route path="/2" element={<Page2 />} /> <Route path="*" element={<Navigate to="/1" />} /> </Routes> </CSSTransition> </SwitchTransition> ); }
index.js
import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import { MemoryRouter } from "react-router-dom"; import App from "./App"; const rootElement = document.getElementById("root"); const root = createRoot(rootElement); root.render( <StrictMode> <MemoryRouter> <App /> </MemoryRouter> </StrictMode> );
问题分析与解决方案
你的实现存在逻辑错误,核心原因是顶层路由的转场逻辑误触发了嵌套路由所在组件的卸载。
问题根源
在App组件中,你将整个<Routes>包裹在<CSSTransition>内,并使用location.key作为转场组件的key。嵌套路由切换时,location.key会更新,导致<CSSTransition>重新渲染,进而触发顶层<Routes>的完整重渲染——这会直接卸载并重新挂载Page1组件,包裹它的MyContextProvider自然也会跟着卸载,状态因此丢失。
而不使用转场时,React Router会智能复用匹配的组件(Page1不会被卸载),所以Context Provider能保持挂载状态。
修复方案
需要区分顶层路由转场和嵌套路由转场,将转场逻辑拆分到对应层级:
- 仅给顶层路由(Page1 ↔ Page2)添加转场
- 嵌套路由的转场单独处理(按需添加),确保嵌套路由所在的Page1组件不受顶层转场逻辑影响
修改后的核心代码示例:
export default function App() { const location = useLocation(); // 仅获取顶层路由匹配结果,避免嵌套路由切换触发顶层转场 const matches = useMatches(); const rootMatch = matches[0]; return ( <SwitchTransition> <CSSTransition // 用顶层路由的pathname作为key,仅顶层路由切换时触发转场 key={rootMatch.pathname} classNames="right-to-left" timeout={200} > <Routes location={location}> {/* 调整路由配置,明确嵌套路由层级 */} <Route path="/1/*" element={<Page1 />} /> <Route path="/2" element={<Page2 />} /> <Route path="*" element={<Navigate to="/1" />} /> </Routes> </CSSTransition> </SwitchTransition> ); } // Page1组件内的嵌套路由调整(避免路径冲突) const Page1 = () => { const location = useLocation(); return ( <MyContextProvider> <h1>Page1</h1> <NavLink to="/2">Go to Page2</NavLink> {/* 嵌套路由可单独添加转场,不影响Context状态 */} <SwitchTransition> <CSSTransition key={location.key} classNames="fade" timeout={200}> <Routes location={location}> <Route path="/1" element={<SubPage1 />} /> <Route path="/1/sub" element={<SubPage2 />} /> <Route path="*" element={<Navigate to="/1" />} /> </Routes> </CSSTransition> </SwitchTransition> </MyContextProvider> ); };
关键修改点
- 顶层转场的
key改为rootMatch.pathname,仅当顶层路由(/1、/2)切换时触发转场,嵌套路由切换时rootMatch.pathname不变,不会触发顶层组件重渲染 - 调整嵌套路由路径(如/1/sub),避免与顶层路由路径冲突,同时明确路由层级
- 嵌套路由的转场可单独实现,不影响顶层Context的挂载状态
这样既能实现顶层路由的转场效果,又能保证嵌套路由切换时Context Provider不会被卸载,状态得以保留。
内容的提问来源于stack exchange,提问作者jdimmerman
相关产品推荐
相关产品推荐

