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

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">&rarr;</span>
          </a>
        ) : (
          <a
            onClick={() => setUser(undefined)}
            href="/"
            className="text-sm font-semibold leading-6 text-black-900"
          >
            Log out <span aria-hidden="true">&rarr;</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">&rarr;</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">&rarr;</span>
          </button>
        )}
      </div>
    </header>
  );
}

内容的提问来源于stack exchange,提问作者user23351951

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:34:56