React Router中useLocation上下文错误及全局组件实现方案咨询
解决useLocation必须在Router上下文使用的问题
问题原因
useLocation 钩子依赖React Router提供的上下文,当前EDProgressBar组件被放在RouterProvider外部,不在上下文范围内,因此抛出错误:
Uncaught Error: useLocation() may be used only in the context of a
component.
同时需求要求EDProgressBar全局存在,不受路由切换影响,而RouterProvider不支持直接传入子组件。
解决方案:创建全局根布局
通过创建根布局组件,将全局UI组件(包括EDProgressBar)放入路由上下文,同时保留全局显示的特性。
步骤1:创建根布局组件
新建根布局文件(比如@/layouts/RootLayout.tsx),将全局组件和路由出口Outlet组合:
import React from 'react'; import { Outlet } from 'react-router-dom'; import EDNotification from '@/ui/EDNotification'; import EDProgressBar from '@/ui/EDProgressBar'; const RootLayout: React.FC = () => { return ( <> {/* Outlet用于渲染当前匹配的路由页面内容 */} <Outlet /> <div id="backdrop-root" /> <div id="overlay-root" /> <EDNotification /> <EDProgressBar /> </> ); }; export default RootLayout;
步骤2:修改路由构建器
更新RouterBuilder,将所有路由作为根布局的子路由,确保全局组件处于Router上下文内:
import React from 'react'; import { type RouteObject } from 'react-router-dom'; import RootLayout from '@/layouts/RootLayout'; // 导入根布局 const CliAuth = React.lazy(() => import('./pages/CliAuth')); const CliAuthenticated = React.lazy(() => import('./pages/CliAuthenticated')); const NotFound = React.lazy(() => import('./pages/NotFound')); const RouterBuilder = (isAuthenticated: boolean | null) => { const generalRoutes: RouteObject[] = [ { path: 'cli-auth', element: <CliAuth />, }, { path: 'cli-authenticated', element: <CliAuthenticated />, }, { path: 'not-found', element: <NotFound />, }, { path: '*', element: isAuthenticated === null ? null : <NotFound />, }, ]; // 将所有路由包裹在根布局下 return [ { path: '/', element: <RootLayout />, children: [...(isAuthenticated ? authorizedRoutes : unAuthorizedRoutes), ...generalRoutes], }, ]; }; export default RouterBuilder;
步骤3:简化AppView组件
移除AppView中原来的全局组件引用,因为它们已经被移入根布局:
import React, { Suspense, useMemo } from 'react'; import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import RouterBuilder from './App.router'; interface IProps { readonly isAuthenticated: boolean | null; } const AppView: React.FC<IProps> = (props: React.PropsWithChildren<IProps>) => { const routes = useMemo(() => { return RouterBuilder(props.isAuthenticated); }, [props.isAuthenticated]); return ( <Suspense fallback={null}> <RouterProvider router={createBrowserRouter(routes)} fallbackElement={null} /> </Suspense> ); }; AppView.displayName = 'AppView'; AppView.defaultProps = {}; export default React.memo(AppView);
效果说明
修改后:
EDProgressBar处于Router上下文内,可正常使用useLocation监听路由变化- 根布局全局生效,
EDProgressBar会始终显示在页面上,不受具体路由组件的影响 - 符合React Router v6的路由嵌套设计规范
内容的提问来源于stack exchange,提问作者Tal Rofe
相关产品推荐
相关产品推荐

