createBrowserRouter 和 RouterProvider 应如何与应用程序上下文一起使用

Ben*_*ier 4 typescript reactjs react-router-dom

任何大型应用程序都会有可能需要路由器重定向用户的一般上下文。由于RouterProvider只能在根级别使用。如何将它与应用程序上下文一起使用。

例如,ApiContext如果用户无权访问 API,我会重定向到 403 路由/组件。

如何将此上下文包装在 中,而RouterProvider不必在每次路由更改时重新渲染它。换句话说。我怎样才能拥有使用路由器的父上下文?

如果我使用 ,它工作得很好BrowserRouter,但在 v6.8 中卸载之前使用任何类型的提示的唯一方法是使用数据 api 和方法createBrowserRouter

标题导航也是如此。我不希望我的标头出现在每个路由组件和路由的一部分中。我希望我的标头是静态的,而路由组件是动态的。

// Aplication Context
export const ApiContext = React.createContext({});

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

  if (!token) {
    navigate('/403');
  }

  const value = useMemo(() => ({}), []);
  return (
    <ApiContext.Provider value={value}>
      {p.children}
    </ApiContext.Provider>
  );
});
Run Code Online (Sandbox Code Playgroud)
// Index
const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    children: [
      {
        path: "403",
        element: <Login />,
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <ApiProvider>
    <Header />
    <RouterProvider router={router} />
  </ApiProvider>
);
Run Code Online (Sandbox Code Playgroud)

Dre*_*ese 8

事实上,它是RouterProvider 应用程序的根节点/元素/组件,它只需要在 ReactTree 中比任何需要访问它提供的路由上下文的渲染组件更高。

如果你正在渲染的组件需要访问路由器提供的路由上下文,例如useNavigate,等等,那么它们必然需要在路由器提供者创建的ReactTree下渲染,即路由器组件。

将其移至布局路线ApiProvider上的路由器中。该组件现在还应该渲染嵌套路由以将其内容渲染到其中,而不是prop。ApiProviderOutletelementchildren

ApiProvider组件也不应该navigate直接在组件主体中作为无意的副作用进行调用。将if (!token)检查和重定向移至useEffect挂钩中,或者仅使用组件呈现重定向Navigate

例子:

应用上下文

import { Navigate, Outlet } from 'react-router-dom';

export const ApiContext = React.createContext({});

export const ApiProvider = () => {
  const [token, setToken] = useState<string>("");

  const value = useMemo(() => ({}), []);

  if (!token) {
    return <Navigate to="/403" replace />;
  }

  return (
    <ApiContext.Provider value={value}>
      <Header />
      <Outlet />
    </ApiContext.Provider>
  );
};
Run Code Online (Sandbox Code Playgroud)

指数

const router = createBrowserRouter([
  {
    element: <ApiProvider />,
    children: [
      {
        path: "/",
        element: <Root />,
        loader: rootLoader,
        children: [
          {
            path: "team",
            element: <Team />,
            loader: teamLoader,
          },
        ],
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);
Run Code Online (Sandbox Code Playgroud)