You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React应用Protected Route异常:访问受限路由先进入再跳转登录

问题根源

未授权访问受限路由时会短暂进入目标页面再跳转登录,核心原因是:

  1. ProtectedRouteHook 中 isUser、isAdmin 初始值为 undefined
  2. ProtectedRoute 仅在 auth === false 时触发跳转,初始 undefined 会直接放行渲染子路由组件
  3. 等到 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 07:37:36