You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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能保持挂载状态。

修复方案

需要区分顶层路由转场和嵌套路由转场,将转场逻辑拆分到对应层级:

  1. 仅给顶层路由(Page1 ↔ Page2)添加转场
  2. 嵌套路由的转场单独处理(按需添加),确保嵌套路由所在的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 16:11:16