React登录页useAuth钩子authenticated值反复切换问题求助
React登录页面Auth钩子状态异常:authenticated值反复切换
我用React开发登录页面时,在创建Context和认证钩子useAuth时遇到问题:authenticated的值无法保持为true,会在true和false之间交替切换,排查后没发现循环等异常情况。
相关代码如下:
import backend from "../utils/backend"; import { useState, useEffect } from "react"; export default function useAuth() { const [authenticated, setAuthenticated] = useState(false); useEffect(() => { console.log(authenticated); const token = localStorage.getItem("token"); if (token && !authenticated) { authUser(token); } }, []); setInterval(() => { console.log(authenticated); }, 300); async function authUser(token) { backend.defaults.headers.Authorization = `Bearer ${JSON.parse(token)}`; try { const data = await backend.get("/users/verify").then((res) => { return res.data; }); console.log(data, "response from back-end"); setAuthenticated(data); } catch (error) { console.log(error); } } return { authenticated }; }
控制台输出显示authenticated在true和false之间交替变化。我试过重构系统、直接在authUser里调用setAuthenticated(true),都没解决问题。
问题分析
- 闭包与定时器残留:钩子每次渲染都会创建新的
setInterval,旧定时器未被清理,且每个定时器捕获的是创建时的authenticated闭包值,多个定时器同时输出不同状态值,造成“状态切换”的假象。 - useEffect依赖缺失:
useEffect依赖数组为空,内部始终引用初始的authenticated值(false),若钩子重复执行(比如Provider包裹的组件重渲染),会重复触发authUser请求,可能导致状态反复更新。 - 无请求防重复机制:没有加载状态控制,可能多次调用
authUser,引发多次状态更新。
解决方案
修改后的代码如下:
import backend from "../utils/backend"; import { useState, useEffect, useCallback } from "react"; export default function useAuth() { const [authenticated, setAuthenticated] = useState(false); const [isLoading, setIsLoading] = useState(true); // 用useCallback稳定函数引用,避免重复创建 const authUser = useCallback(async (token) => { setIsLoading(true); backend.defaults.headers.Authorization = `Bearer ${JSON.parse(token)}`; try { const res = await backend.get("/users/verify"); const data = res.data; console.log(data, "response from back-end"); setAuthenticated(data); } catch (error) { console.log(error); setAuthenticated(false); } finally { setIsLoading(false); } }, []); useEffect(() => { const token = localStorage.getItem("token"); // 仅在有token、未认证且未加载时触发请求 if (token && !authenticated && !isLoading) { authUser(token); } }, [authenticated, isLoading, authUser]); useEffect(() => { // 定时器放入useEffect,监听authenticated确保捕获最新状态 const interval = setInterval(() => { console.log(authenticated); }, 300); // 卸载时清理定时器,避免残留 return () => clearInterval(interval); }, [authenticated]); return { authenticated, isLoading }; }
关键修改说明:
- 用
useCallback包裹authUser,稳定函数引用,作为useEffect依赖避免无效触发。 - 新增
isLoading状态,防止重复发起认证请求。 - 将定时器移入
useEffect并添加清理函数,确保仅存在一个定时器,且始终捕获最新的authenticated值。 - 补全
useEffect依赖数组,确保状态更新后逻辑能正确执行。
内容的提问来源于stack exchange,提问作者Giovanni Carvalho de Oliveira
相关产品推荐
相关产品推荐

