React自定义Auth Hook及私有路由引发Maximum update depth exceeded错误求助
解决React中Maximum update depth exceeded错误(私有路由+Firebase Auth场景)
问题重现
自定义Auth状态Hook及私有路由代码如下:
自定义Hook useAuthStatus
import { getAuth, onAuthStateChanged } from 'firebase/auth' import { useState, useEffect } from 'react' export function useAuthStatus() { const [loggedIn, setLoggedIn] = useState(false) const [checkingStatus, setCheckingStatus] = useState(true) useEffect(()=>{ const auth = getAuth() onAuthStateChanged(auth , (user)=>{ if(user){ setLoggedIn(true) } setCheckingStatus(false) }) console.log("hello") }, []) return {loggedIn, checkingStatus} }
私有路由组件 PrivateRoute
import {useAuthStatus} from '../hooks/useAuthStatus' import { Outlet , Navigate} from 'react-router-dom' function PrivateRoute() { const {loggedIn, checkingStatus} = useAuthStatus(); if(checkingStatus){ return <div>looding...</div> } return loggedIn ? <Outlet/> : <Navigate to="/auth/sign-in"/> } export default PrivateRoute
运行时触发错误:Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array
错误原因分析
- 无限路由跳转循环:若
PrivateRoute被错误应用到了/auth/sign-in等登录页面路由上,会形成死循环:未登录时触发跳转至/sign-in,而/sign-in路由又被PrivateRoute保护,再次检查登录状态并跳转,最终触发更新深度超限。 - 未清理Firebase Auth订阅:
onAuthStateChanged会持续监听Auth状态,若不主动取消订阅,可能导致内存泄漏,甚至在组件卸载后触发状态更新引发异常渲染。
修复方案
1. 修正路由配置,隔离私有/公开路由
确保PrivateRoute仅包裹需要登录权限的页面(如仪表盘),登录页面等公开路由直接配置,不被PrivateRoute嵌套:
import { Routes, Route } from 'react-router-dom'; import PrivateRoute from './PrivateRoute'; import Dashboard from './Dashboard'; import SignIn from './auth/SignIn'; function App() { return ( <Routes> {/* 公开路由:无需登录即可访问 */} <Route path="/auth/sign-in" element={<SignIn />} /> {/* 私有路由:仅登录用户可访问 */} <Route element={<PrivateRoute />}> <Route path="/dashboard" element={<Dashboard />} /> {/* 其他需要权限的路由 */} </Route> </Routes> ); }
2. 清理Firebase Auth订阅
在useAuthStatus的useEffect中添加订阅清理逻辑,避免内存泄漏:
import { getAuth, onAuthStateChanged } from 'firebase/auth' import { useState, useEffect } from 'react' export function useAuthStatus() { const [loggedIn, setLoggedIn] = useState(false) const [checkingStatus, setCheckingStatus] = useState(true) useEffect(()=>{ const auth = getAuth() // 保存取消订阅函数 const unsubscribe = onAuthStateChanged(auth , (user)=>{ setLoggedIn(!!user); // 简化状态更新逻辑,直接将user转为布尔值 setCheckingStatus(false) }) // 组件卸载时取消订阅 return () => unsubscribe(); }, []) return {loggedIn, checkingStatus} }
3. 优化私有路由组件
修正拼写错误,并添加replace属性避免浏览器历史记录堆积:
import {useAuthStatus} from '../hooks/useAuthStatus' import { Outlet , Navigate} from 'react-router-dom' function PrivateRoute() { const {loggedIn, checkingStatus} = useAuthStatus(); if(checkingStatus){ return <div>Loading...</div> } return loggedIn ? <Outlet/> : <Navigate to="/auth/sign-in" replace /> } export default PrivateRoute
内容的提问来源于stack exchange,提问作者imen
相关产品推荐
相关产品推荐

