React异步状态异常:Context中isAuth设为true但组件内仍为false
React Context异步状态更新不触发组件权限校验的问题解决
问题根源
- ProjectTable组件的useEffect未监听isAuth变化:你的
useEffect依赖项为空数组,只会在组件首次挂载时执行一次。此时App组件的API认证请求大概率还未完成,拿到的是Context的初始isAuth值(false),后续isAuth更新为true后,该useEffect不会重新触发,导致权限校验逻辑无法执行。 - 组件挂载时机与异步请求时序不匹配:ProjectTable挂载时,异步认证请求还在处理中,此时读取的
isAuth是初始值,后续状态更新无法触发已执行过的校验逻辑。
解决方案一:给useEffect添加isAuth依赖项
修改ProjectTable中的useEffect,将isAuth加入依赖数组,确保每次isAuth状态变化时,重新执行权限校验:
export function ProjectTable({projects}:{projects:Project[]}){ const { t } = useTranslation(); const {isAuth} = useUserContext(); const navigateTo = useNavigate(); useEffect(() => { console.log("isAuth in useEffect ProjectTable",isAuth); if (!isAuth){ navigateTo("/login"); } }, [isAuth, navigateTo]) // 加入isAuth和navigateTo到依赖列表 }
解决方案二:封装权限校验为自定义Hook(更优雅)
把权限校验逻辑抽成可复用的自定义Hook,避免重复代码:
// 自定义权限守卫Hook export function useAuthGuard() { const { isAuth } = useUserContext(); const navigateTo = useNavigate(); useEffect(() => { if (!isAuth) { navigateTo("/login"); } }, [isAuth, navigateTo]); } // 在ProjectTable中使用 export function ProjectTable({projects}:{projects:Project[]}){ const { t } = useTranslation(); useAuthGuard(); // 直接调用Hook完成权限校验 // 组件其他业务逻辑... }
额外优化建议
- 处理API请求异常:在App组件的认证请求中添加错误捕获,避免因网络问题或无效token导致
isAuth状态异常:
useEffect(() => { const token = getToken(); if (!token) { setIsAuth(false); return; } fetch(import.meta.env.VITE_API_URL+"/isAuth", { headers: {Authorization: `Bearer ${token}`} }) .then((res) => { if (!res.ok) throw new Error('认证请求失败'); return res.json(); }) .then((response) => { setIsAuth(response.code === 200); }) .catch(() => { setIsAuth(false); // 可选:直接跳转到登录页 // navigateTo("/login"); }) }, [location.pathname, setIsAuth, getToken])
- 将认证逻辑移到Context内部:把API校验逻辑放在UserContextProvider中,让Context初始化时就完成状态同步,减少组件间的状态流转:
export function UserContextProvider ({children}:{children:React.ReactNode}){ const [isAuth, setIsAuth] = useState(false); const setToken = (token:string) => { window.localStorage.setItem('token', token); } const getToken = () => { return window.localStorage.getItem('token'); } const logout = () => { window.localStorage.removeItem('token'); setIsAuth(false); } // Provider内部处理认证校验 useEffect(() => { const token = getToken(); if (!token) { setIsAuth(false); return; } fetch(import.meta.env.VITE_API_URL+"/isAuth", { headers: {Authorization: `Bearer ${token}`} }) .then((res) => res.json()) .then((response) => { setIsAuth(response.code === 200); }) .catch(() => { setIsAuth(false); }) }, []); console.log("isAuth in UserContextProvider",isAuth); return <UserContext.Provider value={{setToken, getToken, logout, setIsAuth, isAuth }}> {children} </UserContext.Provider> }
内容的提问来源于stack exchange,提问作者Max123456
相关产品推荐
相关产品推荐

