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

React Router v6中用Context API实现路由守卫时上下文取值异常问题

问题原因

你遇到的问题是因为**createBrowserRouter在App组件外部提前创建了路由实例**,路由中用到的ProtectedRoute等组件元素在App的AppContext.Provider渲染前就已初始化,导致这些组件无法进入Provider的作用域,只能获取到Context的默认值,而非Provider提供的动态token状态。

解决方案

有两种可行的修复方式,任选其一即可:

方案一:将路由定义移到App组件内部

把createBrowserRouter的创建逻辑放到App组件中,确保路由组件在AppContext.Provider的作用域内初始化:

修改App.js

import { useState } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import AppContext from './context/AppContext.js';
// 导入所需组件
import Root from './components/Root';
import Home from './components/Home';
import Admin from './components/Admin';
import Login from './components/Login';
import ProtectedRoute from './components/ProtectedRoute';

function App() {
    const [token, setToken] = useState("");
    
    function login(token) {
        setToken(token);
    }

    // 在App组件内部创建路由实例
    const router = createBrowserRouter([
        {
            path: "/",
            element: <Root />,
            children: [
                { path: "home", element: <Home /> },
                { path: "admin", element: <ProtectedRoute element={Admin} /> },
                { path: "login", element: <Login /> }
            ]
        }
    ]);

    return (
        <React.StrictMode>
            <AppContext.Provider value={{ token, login }}>
                <RouterProvider router={router} />
            </AppContext.Provider>
        </React.StrictMode>
    );
}

export default App;

可选:拆分路由配置

如果不想把路由逻辑全部堆在App.js里,可以把路由配置抽成数组单独存放:

routes.js

// 仅导出路由配置数组,不提前创建router实例
import Root from './components/Root';
import Home from './components/Home';
import Admin from './components/Admin';
import Login from './components/Login';
import ProtectedRoute from './components/ProtectedRoute';

export default [
    {
        path: "/",
        element: <Root />,
        children: [
            { path: "home", element: <Home /> },
            { path: "admin", element: <ProtectedRoute element={Admin} /> },
            { path: "login", element: <Login /> }
        ]
    }
];

然后在App.js中导入并创建router

import routes from './routes/routes.js';

// ...

const router = createBrowserRouter(routes);

方案二:将Context Provider移到路由根组件内

把AppContext.Provider放到路由的根组件Root中,让所有子路由组件自动处于Context作用域:

修改Root.js

import { useState } from 'react';
import { Outlet } from 'react-router-dom';
import AppContext from '../context/AppContext.js';

function Root() {
    const [token, setToken] = useState("");
    
    function login(token) {
        setToken(token);
    }

    return (
        <AppContext.Provider value={{ token, login }}>
            {/* 这里可以添加根组件的公共布局(比如导航栏) */}
            <Outlet /> {/* 渲染子路由内容 */}
        </AppContext.Provider>
    );
}

export default Root;

修改App.js

去掉外层的AppContext.Provider,直接渲染RouterProvider即可:

import router from './routes/routes.js';

function App() {
    return (
        <React.StrictMode>
            <RouterProvider router={router} />
        </React.StrictMode>
    );
}

export default App;
额外优化建议
  1. 给ProtectedRoute的Navigate添加replace属性,避免登录后返回登录页的历史记录:
export default function ProtectedRoute({ element: Component }) {
    const { token } = useContext(AppContext);
    return token ? <Component /> : <Navigate to="/login" replace />;
}
  1. 确保Login组件正确调用Context中的login函数:
import { useContext } from 'react';
import AppContext from './context/AppContext.js';

function Login() {
    const { login } = useContext(AppContext);

    const handleLogin = () => {
        // 替换为实际登录逻辑,获取真实token
        const mockToken = "valid-user-token";
        login(mockToken);
    };

    return (
        <div>
            <h2>登录页面</h2>
            <button onClick={handleLogin}>模拟登录</button>
        </div>
    );
}

export default Login;

内容的提问来源于stack exchange,提问作者navix1337

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:45:36