如何在React中使用useRoutes钩子实现私有路由?
React useRoutes 实现私有/公开路由的逻辑拆解与优化
现有代码的逻辑分析
先拆解你当前的实现逻辑:
- 通过
useContext(AuthContext)获取全局共享的登录状态isAuth,这是权限判断的核心依据 - 定义路由数组
routes,其中只有根路径/做了权限校验:已登录渲染<Home />,未登录则通过<Navigate />跳转到登录页 /printers、/items、/login这几个路由没有权限控制,属于公开可访问状态- 调用
useRoutes(routes)生成路由实例并返回,完成路由渲染
但这个写法存在两个问题:一是需要保护的路由(比如/printers)未做权限校验,未登录用户也能直接访问;二是每个私有路由都要写三元运算符,代码重复且不易维护。
私有路由的核心原理
私有路由本质是路由守卫:在渲染目标组件前先拦截,检查用户的登录状态:
- 已登录:正常渲染目标组件
- 未登录:跳转到登录页,同时可以记录当前访问路径,方便登录后跳转回原页面
优化后的实现方案
我们可以封装一个通用的PrivateRoute组件,把权限判断逻辑抽离出来,所有需要保护的路由都复用这个组件:
1. 封装私有路由组件
import { Navigate } from "react-router-dom"; import { useContext } from "react"; import { AuthContext } from "../contexts"; function PrivateRoute({ children }) { const { sessionContextValue } = useContext(AuthContext); const { isAuth } = sessionContextValue; // 未登录时跳转到登录页,同时记录当前访问路径 if (!isAuth) { return <Navigate to="/login" replace state={{ from: window.location.pathname }} />; } // 已登录则渲染目标组件 return children; } export default PrivateRoute;
2. 重构Routes组件
用PrivateRoute包裹所有需要保护的路由,公开路由直接渲染即可:
import Home from "../pages/home" import Items from "../pages/itens" import Login from "../pages/login" import Printers from "../pages/printers" import { useRoutes } from "react-router-dom" import PrivateRoute from "./PrivateRoute"; function Routes() { const routes = [ { path: '/', element: ( <PrivateRoute> <Home /> </PrivateRoute> ), }, { path: '/printers', element: ( <PrivateRoute> <Printers /> </PrivateRoute> ) }, { path: '/items', element: ( <PrivateRoute> <Items /> </PrivateRoute> ) }, { path: '/login', element: <Login /> } ] return useRoutes(routes) } export default Routes
关键细节解释
- PrivateRoute复用性:把权限判断逻辑抽离后,所有需要保护的路由只需要用
PrivateRoute包裹,避免重复代码,后续修改权限规则只需改这一个组件 - Navigate的replace属性:使用
replace而非push,是为了避免在浏览器历史记录中留下未登录时的访问痕迹,用户登录后返回不会再回到未授权页面 - state传递原路径:跳转到登录页时,通过
state记录用户原本访问的路径,登录成功后可以从state中取出路径跳转回去,提升体验(示例:在Login组件中获取原路径):import { useNavigate, useLocation } from "react-router-dom"; function Login() { const navigate = useNavigate(); const location = useLocation(); // 取原路径,默认跳首页 const from = location.state?.from || '/'; // 登录成功后执行跳转 const handleLogin = () => { // ...登录逻辑 navigate(from, { replace: true }); } return <div>登录组件</div> } - 公开路由的反向处理:如果需要避免已登录用户访问登录页,可以给
/login路由加反向判断:{ path: '/login', element: isAuth ? <Navigate to="/" replace /> : <Login /> }
内容的提问来源于stack exchange,提问作者Bruno Bispo
相关产品推荐
相关产品推荐

