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

Next.js App Router + Auth0登录后导航栏用户信息不持久问题求助

解决Next.js App Router中Auth0登录状态刷新/跳转丢失问题

问题根源在于当前AuthContext的用户状态仅存储在组件内存中,页面刷新或硬跳转后组件重新挂载,状态会被重置为初始值null,导致用户信息消失。需要将状态持久化到localStorage,并在组件初始化时恢复。

解决方案步骤

  • 登录成功后将用户信息和accessToken存入localStorage
  • 在AuthProvider初始化时从localStorage读取并恢复状态
  • 登出时清除localStorage中的对应数据
  • 可选:用Next.js的Link组件替换原生a标签,避免全页面刷新优化体验

修改后的AuthContext.tsx代码

// AuthContext.tsx
'use client';
import React, { createContext, useContext, useState, ReactNode } from 'react';

interface User {
  email: string;
  name: string;
  picture: string;
  nickname?: string; // 补充nickname字段,避免Navbar报错
}

interface AuthContextType {
  user: User | null;
  accessToken: string | null;
  login: (userData: { user: User; accessToken: string }) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  // 从localStorage初始化状态,仅执行一次
  const [user, setUser] = useState<User | null>(() => {
    if (typeof window !== 'undefined') {
      const savedUser = localStorage.getItem('auth_user');
      return savedUser ? JSON.parse(savedUser) : null;
    }
    return null;
  });
  
  const [accessToken, setAccessToken] = useState<string | null>(() => {
    if (typeof window !== 'undefined') {
      return localStorage.getItem('auth_token') || null;
    }
    return null;
  });

  const login = (userData: { user: User; accessToken: string }) => {
    console.log('User logging in:', userData.user);
    setUser(userData.user);
    setAccessToken(userData.accessToken);
    // 持久化到localStorage
    localStorage.setItem('auth_user', JSON.stringify(userData.user));
    localStorage.setItem('auth_token', userData.accessToken);
  };

  const logout = () => {
    console.log('User logging out');
    setUser(null);
    setAccessToken(null);
    // 清除localStorage数据
    localStorage.removeItem('auth_user');
    localStorage.removeItem('auth_token');

    if (typeof window !== 'undefined') {
      const returnTo = encodeURIComponent(window.location.origin);
      window.location.href = `https://${process.env.NEXT_PUBLIC_AUTH0_DOMAIN}/v2/logout?client_id=${process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID}&returnTo=${returnTo}`;
    }
  };

  return (
    <AuthContext.Provider value={{ user, accessToken, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

优化后的Navbar.tsx代码(可选)

// Navbar.tsx
'use client';

import { useAuth } from '@/app/context/AuthContext';
import Link from 'next/link'; // 引入Next.js客户端导航组件

const Navbar = () => {
  const { user, logout } = useAuth();
  console.log('Navbar user state:', user);

  return (
    <nav className="sticky top-0 z-[999] w-full bg-gray-800">
      <div className="px-2 lg:px-8">
        <div className="flex h-16 items-center justify-between">
          <div className="flex items-center justify-center">
            <div className="flex items-start">
              <Link href="/" className="font-mono text-2xl text-white">Fake Store</Link>
            </div>
          </div>
          <div className="flex items-center gap-2">
            <button
              type="button"
              className="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none"
            >
              <Link href="/shopping">Products</Link>
            </button>
            <div className="flex items-center pr-2">
              <button
                type="button"
                className="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none"
              >
                <Link href="/cart">Cart</Link>
              </button>
            </div>
            {user && (
              <div className="flex items-center">
                <img src={user.picture} alt={user.name} className="h-8 w-8 rounded-full" />
                <span className="ml-2 text-white">Hi {user.nickname || user.name}!</span> {/* 兼容nickname不存在的情况 */}
                <button
                  onClick={logout}
                  className="ml-4 rounded-full bg-red-500 px-4 py-1 text-white"
                >
                  Logout
                </button>
              </div>
            )}
          </div>
        </div>
      </div>
    </nav>
  );
};

export default Navbar;

内容的提问来源于stack exchange,提问作者M-Sev1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:53:11