React路由鉴权报错:Maximum update depth exceeded问题修复求助
问题描述
需要在app.js中实现以下路由鉴权逻辑:
- 先检查用户是否已登录:已登录则跳转目标URL;未登录则自动跳转到
/connexion登录页 - 用户已登录时,进一步检查profile完善状态:状态为
finished则跳转目标URL;否则跳转到/information资料完善页
原本仅做登录检查时功能正常,但添加资料完善检查后,出现Warning: Maximum update depth exceeded错误,推测是useEffect重复触发导致,但不知道具体修复方法,相关代码如下:
const App = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); const [tokenChecked, setTokenChecked] = useState(false); const { profile } = useStateContext(); useEffect(() => { const token = sessionStorage.getItem("token"); setIsLoggedIn(token ? true : false); setTokenChecked(true); }, []); return ( <Router> {tokenChecked ? ( <Routes> <Route element={<NavbarLayout />}> <Route path="/" exact element={<HomePage />} /> <Route path="/about" exact element={<AboutPage />} /> <Route path="/how-it-works" exact element={<HowItWorks />} /> </Route> <Route element={<NoNavbarLayout />}> {/*<Route path="/register-type" exact element={<LoginPick />} />*/} <Route path="/connexion" exact element={<UserLogin />} /> <Route path="/récupérer" exact element={<UserRecovery />} /> <Route path="/register-user" exact element={<UserRegister />} /> <Route path="/register-tech" exact element={<TechnicianRegister />} /> <Route path="/register-admin" exact element={<AdminRegister />} /> </Route> <Route element={<ConfirmLayout />}> <Route path="/confirmation" exact element={<AccountConfirmation />} /> <Route path="/nonconnecte" exact element={<UserRedirectLMsg />} /> <Route path="/validation/:id/:confirm" exact element={<AccountValidation />} /> </Route> {isLoggedIn === true ? ( <> {profile === 'finished' ? ( <> <Route element={<ConfirmLayout />}> <Route path="/encours" exact element={<UserPending />} /> <Route path="/information" exact element={ <Elements stripe={stripePromise}> <UserInfo /> </Elements> } /> </Route> <Route element={<DashboardLayout />}> <Route path="/nouvel-appareil" exact element={<UserNewDevice />} /> <Route path="/new-project" exact element={<NewProject />} /> <Route path="/demande-contrat" exact element={<NewContract />} /> <Route path="/user-dash" exact element={<UserDash />} /> <Route path="/profile" exact element={<UserProfile />} /> <Route path="/user-history" exact element={<UserHistory />} /> <Route path="/navigation" exact element={<Compte />} /> <Route path="/user-notification" exact element={<UserNotification />} /> <Route path="/user-projets-details/:id" exact element={<UserProjectDetails />} /> <Route path="/user-intervention-details/:id" exact element={<InterventionDetail />} /> <Route path="/user-details/:id" exact element={<UserDetail />} /> <Route path="/user-claim-details/:id" exact element={<UserClaimsDetail />} /> <Route path="/reclamations" exact element={<UserClaims />} /> <Route path="/browse" exact element={<SearchGrid />} /> <Route path="/technician-dash" exact element={<TechnicianDash />} /> <Route path="/technician-profile" exact element={<TechnicianProfile />} /> <Route path="/technician-projets" exact element={<TechnicianProjects />} /> <Route path="/technician-history" exact element={<UserHistory />} /> <Route path="/technician-projets-details/:id" exact element={<UserProjectDetails />} /> <Route path="/product/:id" exact element={<UserNewProductDetail />} /> <Route path="/admin-notification" exact element={<AdminNotification />} /> <Route path="/new-user" exact element={<AddUser />} /> <Route path="/litiges" exact element={<Litige />} /> <Route path="/réclamations" exact element={<Reclamation />} /> <Route path="/users" exact element={<UsersList />} /> </Route> <Route element={<NoNavbarLayout />}> <Route path="/register-type" element={<Navigate to="/navigation" />} /> </Route> </> ):(<> <Route path="*" element={<Navigate to="/information" replace />} /> </>)} </> ) : isLoggedIn === false ? ( <> <Route path="*" element={<Navigate to="/connexion" replace />} /> </> ) : null} </Routes> ) : null} </Router> ); }; export default App;
错误原因
出现无限更新循环的核心原因不是useEffect重复触发,而是路由跳转的无限循环:
当用户profile未完善时,你用path="*"的路由规则匹配所有页面,然后跳转到/information;但/information本身也会被这个通配符路由匹配,导致再次触发跳转,形成无限循环,最终触发Maximum update depth exceeded警告。
另外,当前直接在Routes内部嵌套条件渲染路由的写法,会导致路由规则动态变化,也会加剧渲染不稳定的问题。
修复方案
改用路由守卫组件封装鉴权逻辑,将登录检查、profile完善检查的逻辑抽离成独立组件,避免直接在Routes中动态修改路由规则,同时排除/information页面被通配符路由匹配的情况。
步骤1:创建路由守卫组件
// 登录守卫:检查是否登录,未登录则跳转到登录页 const AuthGuard = ({ children }) => { const token = sessionStorage.getItem("token"); const isLoggedIn = !!token; if (!isLoggedIn) { return <Navigate to="/connexion" replace />; } return children; }; // Profile完善守卫:已登录但profile未完善时,跳转到资料完善页(排除/information本身) const ProfileCompleteGuard = ({ children, location }) => { const { profile } = useStateContext(); // 排除资料完善页本身,避免循环跳转 if (profile !== 'finished' && location.pathname !== '/information') { return <Navigate to="/information" replace />; } return children; };
步骤2:重构App组件的路由结构
将鉴权逻辑通过守卫组件包裹路由,不再在Routes内部做条件渲染:
const App = () => { const [tokenChecked, setTokenChecked] = useState(false); useEffect(() => { // 仅做token存在性检查的初始化,isLoggedIn直接通过token计算,无需存状态 setTokenChecked(true); }, []); return ( <Router> {tokenChecked ? ( <Routes> {/* 无需鉴权的公共页面 */} <Route element={<NavbarLayout />}> <Route path="/" exact element={<HomePage />} /> <Route path="/about" exact element={<AboutPage />} /> <Route path="/how-it-works" exact element={<HowItWorks />} /> </Route> <Route element={<NoNavbarLayout />}> <Route path="/connexion" exact element={<UserLogin />} /> <Route path="/récupérer" exact element={<UserRecovery />} /> <Route path="/register-user" exact element={<UserRegister />} /> <Route path="/register-tech" exact element={<TechnicianRegister />} /> <Route path="/register-admin" exact element={<AdminRegister />} /> </Route> <Route element={<ConfirmLayout />}> <Route path="/confirmation" exact element={<AccountConfirmation />} /> <Route path="/nonconnecte" exact element={<UserRedirectLMsg />} /> <Route path="/validation/:id/:confirm" exact element={<AccountValidation />} /> </Route> {/* 需要登录的路由:用AuthGuard包裹 */} <Route element={<AuthGuard />}> {/* 资料完善页:不需要profile检查(是完善入口) */} <Route element={<ConfirmLayout />}> <Route path="/information" exact element={ <Elements stripe={stripePromise}> <UserInfo /> </Elements> } /> <Route path="/encours" exact element={<UserPending />} /> </Route> {/* 需要profile完善的路由:用ProfileCompleteGuard包裹 */} <Route element={<ProfileCompleteGuard />}> <Route element={<DashboardLayout />}> <Route path="/nouvel-appareil" exact element={<UserNewDevice />} /> <Route path="/new-project" exact element={<NewProject />} /> <Route path="/demande-contrat" exact element={<NewContract />} /> <Route path="/user-dash" exact element={<UserDash />} /> <Route path="/profile" exact element={<UserProfile />} /> <Route path="/user-history" exact element={<UserHistory />} /> <Route path="/navigation" exact element={<Compte />} /> <Route path="/user-notification" exact element={<UserNotification />} /> <Route path="/user-projets-details/:id" exact element={<UserProjectDetails />} /> <Route path="/user-intervention-details/:id" exact element={<InterventionDetail />} /> <Route path="/user-details/:id" exact element={<UserDetail />} /> <Route path="/user-claim-details/:id" exact element={<UserClaimsDetail />} /> <Route path="/reclamations" exact element={<UserClaims />} /> <Route path="/browse" exact element={<SearchGrid />} /> <Route path="/technician-dash" exact element={<TechnicianDash />} /> <Route path="/technician-profile" exact element={<TechnicianProfile />} /> <Route path="/technician-projets" exact element={<TechnicianProjects />} /> <Route path="/technician-history" exact element={<UserHistory />} /> <Route path="/technician-projets-details/:id" exact element={<UserProjectDetails />} /> <Route path="/product/:id" exact element={<UserNewProductDetail />} /> <Route path="/admin-notification" exact element={<AdminNotification />} /> <Route path="/new-user" exact element={<AddUser />} /> <Route path="/litiges" exact element={<Litige />} /> <Route path="/réclamations" exact element={<Reclamation />} /> <Route path="/users" exact element={<UsersList />} /> </Route> <Route element={<NoNavbarLayout />}> <Route path="/register-type" element={<Navigate to="/navigation" replace />} /> </Route> </Route> </Route> {/* 404或未匹配路由处理 */} <Route path="*" element={<Navigate to="/" replace />} /> </Routes> ) : null} </Router> ); }; export default App;
额外优化点
- 移除了
isLoggedIn状态,直接通过sessionStorage.getItem("token")计算,避免状态不同步问题 - 将鉴权逻辑抽离到守卫组件,代码结构更清晰,复用性更强
- 明确区分了无需鉴权、仅需登录、需登录且profile完善的路由层级,避免路由规则冲突
内容的提问来源于stack exchange,提问作者Ons Jannet
相关产品推荐
相关产品推荐

