Next.js使用Context API管理认证会话刷新跳转异常问题优化
问题描述
后端运行正常,问题出在useEffect钩子上。页面刷新时,第二个useEffect会输出“not found user”——因为user值初始为null,会立即触发跳转到登录页,之后user才会更新为有效的用户对象。需要遵循行业最佳实践修改AuthContext代码来解决该问题,而非采用临时修复方案。
原代码
"use client" import React from 'react' import { createContext, useState,useEffect } from "react"; import { useRouter } from 'next/navigation'; import API_BASE_URL from '../utils/baseURL' import { usePathname } from 'next/navigation'; import axios from 'axios'; export const userContext = createContext(null); function AuthContext({ children }) { const router=useRouter() const pathname = usePathname(); const [user, setUser] = useState(null); const[loadingAuth,setLoadingAuth] = useState(true); const openRoutes=['/login','/signup'] useEffect(() => { const authtoken = localStorage.getItem('token'); console.log("in ue1") if (authtoken) { axios.get(`${API_BASE_URL}/api/auth`, { headers: { 'authorization': authtoken } }) .then(response => { console.log(response.data) setUser(response.data.user); }) .catch(error => { console.error('Error fetching user data:', error); setUser(null); }) .finally(() => { setLoadingAuth(false); }); } else { setUser(null); setLoadingAuth(false); } }, []); useEffect(() => { console.log("useruseruser",user) if (!user && !openRoutes.includes(pathname)) { console.log("not found user") router.push('/login'); } }, [!loadingAuth,user]); const logout = () => { setUser(null); localStorage.removeItem('token'); router.push('/login'); }; return ( <userContext.Provider value={{ user, setUser , logout, loadingAuth}}> {loadingAuth?<div className=''>Loading.......</div>:children} </userContext.Provider> ); } export default AuthContext;
解决方案(最佳实践)
核心问题是在身份验证状态未确定前(loadingAuth为true时),就执行了路由跳转逻辑。我们需要调整逻辑,确保只有在身份验证加载完成后,才根据user状态判断是否跳转,同时避免初始null值误触发跳转。
修改后的代码如下:
"use client" import React from 'react' import { createContext, useState, useEffect } from "react"; import { useRouter } from 'next/navigation'; import API_BASE_URL from '../utils/baseURL' import { usePathname } from 'next/navigation'; import axios from 'axios'; export const UserContext = createContext(null); // 规范命名:首字母大写 function AuthContext({ children }) { const router = useRouter(); const pathname = usePathname(); const [user, setUser] = useState(null); const [loadingAuth, setLoadingAuth] = useState(true); const openRoutes = ['/login', '/signup']; useEffect(() => { const authenticateUser = async () => { const authtoken = localStorage.getItem('token'); try { if (authtoken) { const response = await axios.get(`${API_BASE_URL}/api/auth`, { headers: { 'authorization': authtoken } }); setUser(response.data.user); } else { setUser(null); } } catch (error) { console.error('Error fetching user data:', error); setUser(null); // 如果token无效,直接清除 localStorage.removeItem('token'); } finally { setLoadingAuth(false); } }; authenticateUser(); }, []); useEffect(() => { // 仅在加载完成后执行路由判断 if (!loadingAuth) { const isOpenRoute = openRoutes.includes(pathname); // 未登录且不在开放路由时跳转登录页 if (!user && !isOpenRoute) { router.push('/login'); } // 已登录但在开放路由时,跳转到主页(可选,根据业务需求调整) if (user && isOpenRoute) { router.push('/'); } } }, [loadingAuth, user, pathname, router]); const logout = () => { setUser(null); localStorage.removeItem('token'); router.push('/login'); }; return ( <UserContext.Provider value={{ user, setUser, logout, loadingAuth }}> {/* 加载状态可以用更友好的组件替代,比如骨架屏 */} {loadingAuth ? <div className=''>Loading.......</div> : children} </UserContext.Provider> ); } export default AuthContext;
关键优化点
- 异步逻辑改用
async/await:比then/catch更易读,符合现代JS最佳实践。 - 路由判断时机控制:只有当
loadingAuth为false时才执行路由跳转,避免初始user: null的误触发。 - 增加已登录用户在开放路由的跳转逻辑:如果已登录用户访问登录/注册页,自动跳转到主页,提升用户体验。
- 无效token处理:请求失败时清除本地存储的token,避免下次启动时重复使用无效凭证。
- 规范命名:将
userContext改为UserContext,符合React组件/上下文的命名规范。
内容的提问来源于stack exchange,提问作者Gaurav Bahuguna
相关产品推荐
相关产品推荐

