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
相关产品推荐
相关产品推荐

