空依赖列表下useEffect仍引发无限循环?路由守卫组件故障排查
RequireAuth路由守卫触发无限循环问题排查与解决
问题现象
使用RequireAuth组件作为路由守卫时,访问受保护路由会触发无限循环,控制台被大量getting token日志刷屏,同时出现路由跳转限流、token验证403等错误。
路由守卫代码
import { Navigate, Outlet, useLocation } from "react-router-dom"; import useAuth from "../hooks/useAuth"; import axios from "../api/axios"; import { useEffect, useState } from "react"; const RequireAuth = ({allowedRole}) => { const { getRole, getUser, getToken } = useAuth(); const location = useLocation(); const [isAuthenticated, setIsAuthenticated] = useState(false); useEffect(() => { const token = getToken(); const verifyToken = async () => { try { const response = await axios.get("/api/verifyToken", { headers: { Authorization: `Bearer ${token}`, }, }); setIsAuthenticated(response.data.valid); } catch (error) { console.error("Error verifying token:", error); } }; verifyToken(); }, []); return isAuthenticated ? ( allowedRole?.includes(getRole()) ? ( <Outlet /> ) : getUser() ? (<Navigate to="/unauthorized" state={{ from: location }} replace />) : (<Navigate to="/login" state={{ from: location }} replace />) ) : ( <Navigate to="/login" state={{ from: location }} replace /> ); } export default RequireAuth;
getToken方法实现
const getToken = () => { console.log('getting token'); return auth ? auth?.token : null; };
控制台报错信息
- 重复日志:
AuthProvider.js:49 getting token AuthProvider.js:49 getting token ...(重复输出) - Token验证错误:
GET http://localhost:3500/api/verifyToken 403 (Forbidden) Failed to load resource: the server responded with a status of 403 () Error verifying token: AxiosError - React更新深度超限警告:
react-dom.development.js:86 Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render. - 路由跳转限流提示:
Throttling navigation to prevent the browser from hanging. See https://crbug.com/1038223. Command line switch --disable-ipc-flooding-protection can be used to bypass the protection
问题分析
无限循环根源:
- 路由配置错误:若
/login、/unauthorized等公开路由被RequireAuth包裹,会导致跳转后再次触发路由守卫,形成循环。 - 组件重渲染频繁:AuthProvider中的
getToken、getRole等方法未用useCallback包裹,每次组件渲染都会生成新的函数实例,导致依赖项不稳定,触发重复渲染。 - useEffect依赖缺失:useEffect为空依赖数组,但内部调用了随渲染变化的
getToken方法,可能导致验证逻辑意外重复执行。
- 路由配置错误:若
403错误与后端无日志:
- 请求格式问题:Authorization头的
Bearer与token之间可能缺少空格,或token无效/已过期。 - 后端中间件拦截:认证中间件可能在请求到达
/api/verifyToken路由前就返回了403,未触发后端路由的日志输出。 - CORS配置问题:前端请求可能被浏览器CORS策略拦截,导致实际未到达后端(或后端CORS未正确配置)。
- 请求格式问题:Authorization头的
解决方案
1. 修正路由配置
确保公开路由不被RequireAuth组件包裹,示例如下:
import { Routes, Route } from "react-router-dom"; import RequireAuth from "./components/RequireAuth"; import Login from "./components/Login"; import Unauthorized from "./components/Unauthorized"; import Dashboard from "./components/Dashboard"; function App() { return ( <Routes> {/* 公开路由 */} <Route path="/login" element={<Login />} /> <Route path="/unauthorized" element={<Unauthorized />} /> {/* 受保护路由 */} <Route element={<RequireAuth allowedRole={["admin"]} />}> <Route path="/dashboard" element={<Dashboard />} /> </Route> </Routes> ); } export default App;
2. 优化AuthProvider中的方法
使用useCallback包裹getToken、getRole、getUser,避免每次渲染生成新函数:
import { useCallback, useState, createContext, useContext } from "react"; const AuthContext = createContext({}); export const AuthProvider = ({ children }) => { const [auth, setAuth] = useState(null); const getToken = useCallback(() => { console.log('getting token'); return auth ? auth?.token : null; }, [auth]); const getRole = useCallback(() => { return auth ? auth?.role : null; }, [auth]); const getUser = useCallback(() => { return auth ? auth?.user : null; }, [auth]); return ( <AuthContext.Provider value={{ auth, setAuth, getToken, getRole, getUser }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
3. 修正RequireAuth的useEffect逻辑
补充依赖数组,增加空token判断,确保失败时正确设置认证状态:
useEffect(() => { const token = getToken(); // 空token直接标记为未认证 if (!token) { setIsAuthenticated(false); return; } const verifyToken = async () => { try { const response = await axios.get("/api/verifyToken", { headers: { Authorization: `Bearer ${token}`, // 确保Bearer后有空格 }, }); setIsAuthenticated(response.data.valid); } catch (error) { console.error("Error verifying token:", error); // 验证失败强制标记为未认证 setIsAuthenticated(false); } }; verifyToken(); }, [getToken]); // 加入getToken作为依赖
4. 排查后端403问题
- 检查Authorization头格式:确保
Bearer与token之间存在空格,例如Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...。 - 检查后端认证中间件:确认中间件是否会在token无效时直接返回403,且未记录日志;可在中间件中添加日志输出,排查token验证过程。
- 验证CORS配置:确保后端允许前端请求来源,可通过浏览器网络面板查看请求是否存在CORS相关错误。
内容的提问来源于stack exchange,提问作者daronoh
相关产品推荐
相关产品推荐

