You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 02:14:52