React Context存用户信息后,页面导航时Navbar登录状态异常
解决React AuthContext导航后用户信息丢失的问题
核心问题1:Navbar重复渲染AuthProvider导致上下文隔离
你的Navbar组件内部自行嵌套了<AuthProvider>,这会让Navbar使用独立的上下文实例,而非全局在index.tsx中定义的共享上下文。全局的用户信息更新后,Navbar内的上下文仍保持初始的undefined,因此导航后会错误显示Login选项。
修复方案:删除Navbar中的<AuthProvider>包裹,直接使用全局上下文:
export default function Example() { const [mobileMenuOpen, setMobileMenuOpen] = useState(false); // 使用封装好的useAuth钩子,避免直接调用useContext的潜在问题 const { user, setUser } = useAuth(); return ( // 移除此处的<AuthProvider> <header className="bg-gradient-to-r from-blue-50 via-blue-300 to-blue-500"> {/* 剩余导航代码保持不变 */} <div className="hidden lg:flex lg:flex-1 lg:justify-end"> {user === undefined ? ( <a href="/" className="text-sm font-semibold leading-6 text-black-900" > Log in <span aria-hidden="true">→</span> </a> ) : ( <a onClick={() => setUser(undefined)} href="/" className="text-sm font-semibold leading-6 text-black-900" > Log out <span aria-hidden="true">→</span> </a> )} </div> </header> ); }
核心问题2:useState状态未持久化,组件重挂载后丢失
当前AuthContext用useState存储用户信息,页面刷新或路由切换导致组件重挂载时,useState会重置为初始值undefined,直接导致用户信息丢失。
修复方案:将用户信息持久化到localStorage,初始化时读取缓存,更新时同步写入:
import React, { createContext, useContext, useState, ReactNode, useEffect } from "react"; interface UserData { username: string; password: string; id: string; first_name: string; last_name: string; postal: string; email: string; telephone_number: string; } interface AuthContextProps { user: UserData | undefined; setUser: (userData: UserData | undefined) => void; } export const AuthContext = createContext<AuthContextProps | undefined>(undefined); export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => { // 从localStorage初始化用户信息 const [user, setUser] = useState<UserData | undefined>(() => { const savedUser = localStorage.getItem('authUser'); return savedUser ? JSON.parse(savedUser) : undefined; }); // 用户信息变化时同步到localStorage useEffect(() => { if (user) { localStorage.setItem('authUser', JSON.stringify(user)); } else { localStorage.removeItem('authUser'); } }, [user]); const updateUser = (userData: UserData | undefined) => { setUser(userData); }; return ( <AuthContext.Provider value={{ user, setUser: updateUser }}> {children} </AuthContext.Provider> ); }; export const useAuth = (): AuthContextProps => { const context = useContext(AuthContext); if (!context) { throw new Error("useAuth must be used within an AuthProvider"); } return context; };
额外优化:用React Router的Link替代原生a标签
原生a标签点击会触发页面刷新,即使做了持久化也会导致组件重新初始化。改用React Router的<Link>组件实现无刷新跳转,避免不必要的状态重置:
// 导入Link和useNavigate import { Link, useNavigate } from 'react-router-dom'; export default function Example() { const navigate = useNavigate(); const { user, setUser } = useAuth(); const handleLogout = () => { setUser(undefined); navigate('/'); }; return ( <header className="bg-gradient-to-r from-blue-50 via-blue-300 to-blue-500"> {/* 替换所有导航a标签为Link */} <Popover.Group className="hidden lg:flex lg:gap-x-12"> <Link to="/home" className="text-sm font-semibold leading-6 text-black-900" > Home </Link> <Link to="/about" className="text-sm font-semibold leading-6 text-black-900" > About us </Link> {/* 其他导航项同理替换 */} </Popover.Group> {/* 登出按钮改用button+navigate */} <div className="hidden lg:flex lg:flex-1 lg:justify-end"> {user === undefined ? ( <Link to="/" className="text-sm font-semibold leading-6 text-black-900" > Log in <span aria-hidden="true">→</span> </Link> ) : ( <button onClick={handleLogout} className="text-sm font-semibold leading-6 text-black-900 bg-transparent border-none cursor-pointer" > Log out <span aria-hidden="true">→</span> </button> )} </div> </header> ); }
内容的提问来源于stack exchange,提问作者user23351951
相关产品推荐
相关产品推荐

