React Router v6添加自定义权限路由后页面空白报错
问题根因
你碰到的Uncaught Error: [undefined] is not a <Route> component报错,核心原因是React Router v6对<Routes>的直接子节点做了强校验:只有原生<Route>组件、或者直接包裹Route的<React.Fragment>可以作为<Routes>的直接子元素,你之前尝试的前两种写法(直接把自定义路由组件塞到Routes子级、用自定义组件包裹Route子节点)都违反了这个校验规则,会直接触发渲染报错。
React Router v6已经完全废弃了v5版本自定义Route组件包裹的实现逻辑,权限管控逻辑需要收敛到element属性传入的组件内部实现。
正确实现方案
1. 单路由权限校验(适配公开/私有路由基础需求)
不需要自定义继承Route的组件,直接编写普通包装组件,在组件内部完成权限判断、跳转逻辑,最终返回待渲染的页面内容即可。
- 公开路由组件
BasicRoute.js(未登录可访问,已登录自动跳转业务首页,适用于登录、注册类页面):
import { Navigate, useLocation } from 'react-router-dom'; const BasicRoute = ({ children }) => { // 替换为项目实际的登录态判断逻辑,比如从全局状态、localStorage读取token const isLogin = !!localStorage.getItem('app_token'); const location = useLocation(); if (isLogin) { // 已登录访问公开路由时,跳转至首页或记录的来源页 return <Navigate to="/home" state={{ from: location }} replace />; } // 校验通过,渲染传入的公开页面 return children; }; export default BasicRoute;
- 私有路由组件
AuthRoute.js(登录才可访问,未登录自动跳转登录页,适用于所有受保护的业务页面):
import { Navigate, useLocation } from 'react-router-dom'; const AuthRoute = ({ children }) => { const isLogin = !!localStorage.getItem('app_token'); const location = useLocation(); if (!isLogin) { // 未登录访问受保护路由时,携带当前路径跳转登录页,登录成功后可直接回跳 return <Navigate to="/login" state={{ from: location }} replace />; } // 校验通过,渲染传入的业务页面 return children; }; export default AuthRoute;
App.js路由配置写法(保证所有<Routes>的直接子节点都是原生<Route>):
import { Routes, Route } from 'react-router-dom'; import BasicRoute from './BasicRoute'; import AuthRoute from './AuthRoute'; import Login from './pages/Login'; import Home from './pages/Home'; import UserCenter from './pages/UserCenter'; import NotFound from './pages/NotFound'; function App() { return ( <Routes> {/* 公开路由:登录页 */} <Route path="/login" element={ <BasicRoute> <Login /> </BasicRoute> } /> {/* 私有路由:首页 */} <Route path="/home" element={ <AuthRoute> <Home /> </AuthRoute> } /> {/* 私有路由:个人中心 */} <Route path="/user" element={ <AuthRoute> <UserCenter /> </AuthRoute> } /> {/* 404兜底路由 */} <Route path="*" element={<NotFound />} /> </Routes> ); } export default App;
2. 整组路由统一权限校验(适配父路由嵌套子路由场景)
如果有一整组路由需要复用同一套权限逻辑,不需要给每个路由单独套包装组件,可以把权限组件作为父路由的element,搭配<Outlet />渲染子路由内容。
- 改造支持嵌套场景的
AuthRoute.js:
import { Navigate, useLocation, Outlet } from 'react-router-dom'; const AuthRoute = () => { const isLogin = !!localStorage.getItem('app_token'); const location = useLocation(); if (!isLogin) { return <Navigate to="/login" state={{ from: location }} replace />; } // 嵌套路由场景下,用Outlet渲染匹配到的子路由内容 return <Outlet />; }; export default AuthRoute;
App.js嵌套路由配置写法:
import { Routes, Route } from 'react-router-dom'; import BasicRoute from './BasicRoute'; import AuthRoute from './AuthRoute'; import Login from './pages/Login'; import Home from './pages/Home'; import UserCenter from './pages/UserCenter'; import OrderList from './pages/OrderList'; import NotFound from './pages/NotFound'; function App() { return ( <Routes> <Route path="/login" element={ <BasicRoute> <Login /> </BasicRoute> } /> {/* 所有需要登录的路由统一嵌套在权限父路由下 */} <Route element={<AuthRoute />}> <Route path="/home" element={<Home />} /> <Route path="/user" element={<UserCenter />} /> <Route path="/order" element={<OrderList />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes> ); } export default App;
之前4种错误写法的问题说明
- 写法1:直接将
<AuthRoute>/<BasicRoute>作为<Routes>子组件配置path与element属性:违反Routes子节点校验规则,自定义组件不是原生Route,直接触发类型报错 - 写法2:使用自定义路由组件包裹多个
<Route>子路由节点:同样违反Routes直接子节点校验规则,自定义组件包裹后Routes无法识别合法子节点 - 写法3:将业务页面组件作为子节点传入自定义路由组件,再整体赋值给
<Route>的element属性:一般是自定义组件内部没有正确返回children,或者返回多节点时未用Fragment包裹,导致渲染值为undefined - 写法4:将自定义路由组件作为父路由的element值,嵌套对应权限控制的子路由:一般是自定义组件内部没有渲染
<Outlet />组件,导致子路由内容无法正常输出,触发空白页
小提示:如果按照上述写法调整后仍报相同错误,优先检查自定义路由组件的导入导出是否正确,比如漏写
export default、导入路径错误导致组件值为undefined,也会触发完全相同的报错。
内容的提问来源于stack exchange,提问作者Darkpsinight
相关产品推荐
相关产品推荐

