React组件state不更新:受保护路由loggedIn状态始终为false问题
受保护路由无法正确获取登录状态导致错误重定向
我正在创建仅对已登录用户开放的受保护路由,但在ProtectedRoutes组件中无法正确获取loggedIn状态,该状态始终为false,导致页面被重定向至"/login"。
App.tsx
interface loginContextInterface { loggedIn: boolean; setLoggedIn: (value: (((prevState: boolean) => boolean) | boolean)) => void; } export const LoginContext = createContext({} as loginContextInterface); export default function App() { const [ loggedIn, setLoggedIn ] = useState(false) useEffect(() => { console.log("before", loggedIn) isLoggedIn().then((r) => { console.log("R", r) setLoggedIn(r) }) console.log("after", loggedIn) }, [loggedIn]) return ( <LoginContext.Provider value={{loggedIn, setLoggedIn}}> <Router> <MenuHeader /> <Routes> <Route path="/" element={<Home/>}/> <Route path="/tasks" element={<ProtectedRoutes/>}> <Route path="/" element={<Tasks/>}/> </Route> <Route path="/login" element={<Login />}/> <Route path="/logout" element={<Logout />}/> <Route path="/register" element={<Register/>}/> </Routes> </Router> </LoginContext.Provider> ); }
ProtectedRoutes.tsx
export const ProtectedRoutes = () =>{ const location = useLocation(); const {loggedIn} = useContext(LoginContext) console.log("protected", loggedIn) return ( loggedIn ? <Outlet/> : <Navigate to="/login" replace state={{location}}/> ); }
补充说明
isLoggedIn函数通过调用服务端API,利用Cookie验证用户是否登录。添加日志后,访问/tasks路由并被重定向至/login时,日志输出如下:
VM109:236 protected false App.tsx:21 before false App.tsx:26 after false App.tsx:21 before false App.tsx:26 after false 2App.tsx:23 R true App.tsx:21 before true App.tsx:26 after true App.tsx:23 R true
请问我哪里理解有误?
问题分析与解决
核心问题
- 异步请求时序偏差:组件挂载时
loggedIn初始值为false,isLoggedIn()是异步API请求,在请求返回结果前,ProtectedRoutes已经完成渲染,读取到的是初始false状态,直接触发重定向逻辑。 - useEffect依赖错误:将
loggedIn作为useEffect的依赖项,导致每次loggedIn状态更新都会重新执行isLoggedIn()请求,造成API重复调用(日志中两次出现R true就是这个原因)。
修复步骤
1. 修正useEffect的依赖数组
把useEffect的依赖改为空数组,确保登录状态验证只在组件挂载时执行一次:
useEffect(() => { console.log("before", loggedIn) isLoggedIn().then((r) => { console.log("R", r) setLoggedIn(r) }) console.log("after", loggedIn) }, []) // 移除loggedIn依赖
2. 增加加载状态处理
在登录状态验证的过程中,添加加载状态,避免路由提前执行重定向逻辑:
export default function App() { const [ loggedIn, setLoggedIn ] = useState(false) const [ isLoading, setIsLoading ] = useState(true) // 新增加载状态 useEffect(() => { isLoggedIn().then((r) => { setLoggedIn(r) }).finally(() => { setIsLoading(false) // 请求完成后结束加载 }) }, []) // 加载过程中显示提示,阻止路由渲染 if (isLoading) { return <div>加载中...</div> } return ( <LoginContext.Provider value={{loggedIn, setLoggedIn}}> {/* 原有路由代码 */} </LoginContext.Provider> ); }
3. (可选)在ProtectedRoutes中处理加载状态
如果不想在App层统一处理加载,也可以将isLoading加入Context,在ProtectedRoutes中判断:
// 更新Context接口 interface loginContextInterface { loggedIn: boolean; setLoggedIn: (value: (((prevState: boolean) => boolean) | boolean)) => void; isLoading: boolean; // 新增加载状态字段 } // App中更新Provider的值 <LoginContext.Provider value={{loggedIn, setLoggedIn, isLoading}}> // 修改ProtectedRoutes组件 export const ProtectedRoutes = () =>{ const location = useLocation(); const {loggedIn, isLoading} = useContext(LoginContext) if (isLoading) { return <div>加载中...</div> } return ( loggedIn ? <Outlet/> : <Navigate to="/login" replace state={{location}}/> ); }
内容的提问来源于stack exchange,提问作者Fifips
相关产品推荐
相关产品推荐

