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

如何结合应用上下文使用createBrowserRouter与RouterProvider

解决React Router v6.8+中全局上下文与RouterProvider的结合问题

核心问题分析

你的代码存在两个关键问题:

  • useNavigate 钩子必须在路由上下文(RouterProvider 内部)中使用,外层包裹的 ApiProvider 调用它会直接报错
  • 在渲染阶段直接调用 navigate 会触发无限重渲染循环

同时你需要实现:

  • 全局上下文能控制路由导航(无权限时跳转到403)
  • 静态Header不随路由变更重渲染
  • 使用 createBrowserRouter 以支持数据API相关功能(如卸载前提示)

解决方案步骤

1. 调整组件层级结构

将 ApiProvider 和静态Header放入根路由的Element组件中,确保路由钩子能正常访问,同时Header保持静态:

  • RouterProvider 作为最外层提供路由上下文
  • 根路由的Element包含静态Header、ApiProvider 和 Outlet(用于渲染子路由组件)

2. 修复ApiProvider的导航逻辑

用 useEffect 替代渲染阶段直接调用 navigate,避免无限重渲染;同时用 memo 和 useMemo 优化上下文的重渲染。

完整代码示例

应用上下文(ApiContext)

import { memo, createContext, useState, useEffect, useMemo, useNavigate } from 'react';

export const ApiContext = createContext<{ token?: string; setToken: (token: string | undefined) => void }>({
  setToken: () => {}
});

export const ApiProvider = memo(({ children }: { children: React.ReactNode }) => {
  const navigate = useNavigate();
  const [token, setToken] = useState<string>();

  // 使用useEffect处理导航,避免渲染阶段触发重渲染
  useEffect(() => {
    if (!token && window.location.pathname !== '/403') {
      navigate('/403');
    }
  }, [token, navigate]);

  // 用useMemo确保context值稳定,避免不必要的子组件重渲染
  const contextValue = useMemo(() => ({
    token,
    setToken
  }), [token]);

  return (
    <ApiContext.Provider value={contextValue}>
      {children}
    </ApiContext.Provider>
  );
});

根路由组件(Root)

import { Outlet } from 'react-router-dom';
import { ApiProvider } from './ApiContext';
import { Header } from './Header';

// 用memo包裹根组件,避免无关重渲染
export const Root = memo(() => {
  return (
    <div className="app-container">
      {/* 静态Header,仅在根组件渲染时渲染一次 */}
      <Header />
      {/* ApiProvider包裹子路由组件,确保全局上下文生效 */}
      <ApiProvider>
        <Outlet />
      </ApiProvider>
    </div>
  );
});

入口文件

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Root } from './Root';
import { Login } from './Login';
import { Home } from './Home';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    children: [
      { path: '', element: <Home /> },
      { path: '403', element: <Login /> },
    ],
    // 在这里添加数据API相关配置,比如卸载前提示
    loader: async () => {
      // 示例:加载初始化数据
      return null;
    },
    shouldRevalidate: () => false,
    // 卸载前提示示例
    unstable_onBeforeUnload: () => {
      return '你确定要离开吗?';
    }
  }
]);

ReactDOM.createRoot(document.getElementById('root')!).render(
  <RouterProvider router={router} />
);

关键优化点说明

  • 层级调整:将 ApiProvider 放入路由上下文内部,确保 useNavigate 能正常使用;静态Header作为根组件的一部分,不会随子路由变化重渲染
  • 导航逻辑修复:用 useEffect 监听 token 变化,仅在需要时触发导航,同时判断当前路径避免重复跳转
  • 重渲染优化:用 memo 包裹 ApiProvider 和 Root 组件,用 useMemo 稳定上下文值,减少不必要的组件更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 05:10:31