React应用Protected Route异常:访问受限路由先进入再跳转登录
问题根源
未授权访问受限路由时会短暂进入目标页面再跳转登录,核心原因是:
ProtectedRouteHook中isUser、isAdmin初始值为undefinedProtectedRoute仅在auth === false时触发跳转,初始undefined会直接放行渲染子路由组件- 等到
useEffect执行完毕、设置权限状态为false后才跳转,导致页面闪现
修复方案
1. 优化权限状态钩子(ProtectedRouteHook)
添加加载状态,确保权限判断完成前不渲染路由,同时监听localStorage变化处理登录/登出后的状态更新:
import { useEffect, useState } from 'react' const ProtectedRouteHook = () => { const [userData, setUserData] = useState(JSON.parse(localStorage.getItem("user"))) const [isUser, setIsUser] = useState(null) // 初始null标记未完成权限判断 const [isAdmin, setIsAdmin] = useState(null) const [isLoading, setIsLoading] = useState(true) useEffect(() => { const checkUserRole = () => { const storedUser = JSON.parse(localStorage.getItem("user")); setUserData(storedUser); if (storedUser) { storedUser.role === "user" ? (setIsUser(true), setIsAdmin(false)) : (setIsUser(false), setIsAdmin(true)); } else { setIsUser(false); setIsAdmin(false); } setIsLoading(false); }; checkUserRole(); // 监听localStorage变化,处理跨页登录/登出同步 window.addEventListener('storage', checkUserRole); return () => window.removeEventListener('storage', checkUserRole); }, []) return [isUser, isAdmin, userData, isLoading] } export default ProtectedRouteHook
2. 更新ProtectedRoute组件
在权限判断完成前显示加载状态,严格拦截未授权访问:
import React from 'react' import { Navigate, Outlet } from 'react-router-dom' const ProtectedRoute = ({ auth, isLoading, children }) => { // 加载中显示占位UI(可替换为你的自定义加载组件) if (isLoading) { return <div>加载中...</div>; } // 非授权状态直接跳转登录页 if (!auth) { return <Navigate to="/login" replace />; } return children ? children : <Outlet />; } export default ProtectedRoute
3. 调整App路由使用
传入加载状态,确保权限验证完成后再渲染路由:
function App() { const [isUser, isAdmin, userData, isLoading] = ProtectedRouteHook() return ( <div> <Navbarpage /> <Routes> {/* 公共路由 */} <Route exact path="/" element={<Homepage />} /> <Route path="/allcategory" element={<Allcategories />} /> <Route path="/allbrands" element={<Allbrands />} /> <Route path="/products" element={<Productsview />} /> <Route path="/cart" element={<Cart />} /> <Route path="/order/paymethod" element={<Paymethod />} /> <Route path="/login" element={<Login />} /> <Route path="/register" element={<Register />} /> {/* 用户专属路由 */} <Route element={<ProtectedRoute auth={isUser} isLoading={isLoading}/>} > <Route path="/user/allorders" element={ <Allorders />} /> <Route path="/user/favoriteproducts" element={<Favoriteproducts />} /> <Route path="/user/profile" element={<Profile />} /> <Route path="/user/addresses" element={<Addresses />} /> <Route path="/user/addAddress" element={<AddAddress />} /> <Route path="/user/forgetpassword" element={<Forgetpassword/>}/> <Route path="/user/verifycode" element={<Verifypassword/>} /> <Route path="/user/resetpassword" element={<Resetpassword/>} /> </Route> {/* 管理员专属路由 */} <Route element={<ProtectedRoute auth={isAdmin} isLoading={isLoading}/>}> <Route path="/admin/allproducts" element={<Allproducts />} /> <Route path="/admin/manage-orders" element={<Manageorders />} /> <Route path="/admin/add-brand" element={<Addbrand />} /> <Route path="/admin/add-category" element={<Addcategory />} /> <Route path="/admin/add-subCategory" element={<Addsubcategory />} /> <Route path="/admin/add-product" element={<Addproduct />} /> <Route exact path="/products/:id" element={<Productdetails />} /> <Route exact path="/admin/order/:id" element={<OrderDetails />} /> </Route> </Routes> <Footer /> </div> ); } export default App;
可选优化
- 拆分出
UserProtectedRoute和AdminProtectedRoute两个独立组件,简化路由代码 - 登录/登出时通过自定义事件同步状态,替代localStorage监听(适合单页内操作)
- 将加载状态替换为全局骨架屏,提升用户体验
内容的提问来源于stack exchange,提问作者osman
相关产品推荐
相关产品推荐

