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

React多组件Navbar状态不更新:登录后仅Login组件显示变化

问题描述

我在多个组件中复用了Navbar组件,但当登录状态改变时,仅Login组件内的Navbar会显示更新后的状态(展示LOG OUT),其他组件(如首页Upcoming组件)的Navbar仍显示未登录状态(展示SIGN UP和LOGIN)。登录成功后页面跳转到首页,但首页的Navbar未同步更新状态。

相关组件代码

import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import logo from "../CINEPHILE1.png";

function Navbar({login}) {
  
console.log(login)
  useEffect(() => {
  console.log(login)
  })
  
  
  return (
    <>
    
    
      <nav className="navbar ">
        <div className="container-fluid">
          <Link to="/">
            <img src={logo} alt="" width="100" height="50" />
          </Link>
          <Link
            to="/cinephile/movies"
            className="text-decoration-none text-white "
          >
            MOVIES
          </Link>
          <Link
            to="/cinephile/tv-shows"
            className="text-decoration-none text-white "
          >
            TV SHOWS
          </Link>
        {login? (
          <Link to ="/cinephile/logout" className="text-decoration-none text-white">
          LOG OUT
          </Link> )
          : (
            <>
          <Link
            to="/cinephile/signup"
            className="text-decoration-none text-white "
          >
            SIGN UP
          </Link>
          <Link
            to="/cinephile/login"
            className="text-decoration-none text-white"
          >
            LOGIN
          </Link> 
          </>
        )}
        </div>
      </nav>
    </>
  );
}

export default Navbar;

Login组件

import React, {   useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";

import Navbar from "./Navbar";


function Login() {
const [login,setLogin] =useState(false)
const [email,setEmail] = useState("")
const [password,setPassword] = useState("")
const navigate = useNavigate()
const handleSubmit = async(e) => {
    e.preventDefault()
    try {
        const sendData = await axios.post("http://localhost:5000/cinephile/account/login",{email,password})
        
       
       if(sendData){
        setLogin(true)
          navigate("/")
        }
        
       
    } catch (error) {
       toast("Invalid Credentials")
    }
  }
  
  
  return (  
    <>
     <Navbar login={login}/>

<form onSubmit={handleSubmit}>
  <div className="mb-3">
   Email address
    <input type="email" className="form-control"  aria-describedby="emailHelp" onChange={(e)=>setEmail(e.target.value)} required></input>
   
  </div>
  <div class="mb-3">
   Password
    <input type="password" className="form-control" onChange={(e)=>setPassword(e.target.value)} required></input>
  </div>
  
  <button type="submit" className="btn btn-primary" >LOGIN</button>
  
</form>

</>
    );
}

export default Login;

Home(Upcoming)组件

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import Navbar from "./Navbar";


function Upcoming() {
    const [data,setData] = useState([])
  const fetchData = async() => {
    try {
      const getData = await axios.get("https://api.themoviedb.org/3/movie/upcoming",{
        params: {language: 'en-US', page: '1'},
        headers: {
          accept: 'application/json',
          Authorization: `${process.env.REACT_APP_API_TOKEN}`
          }
          })
          setData(getData.data.results)
          
          } 
          catch (error) {
              
        }
        
        }
        useEffect(()=>{
            fetchData()
            },[])
            
          
            return ( 
              <>
            <Navbar/>
         <main>
      <p className="fst-italic fs-2 text-start text-white">Upcoming</p>
      <div className="images_list">
                {data.map((value,i)=>(
                  <div className="images">
                    <Link to ={`/cinephile/${value.id}`}>
                  <img src={`https://image.tmdb.org/t/p/w500/${value.poster_path}`}  height="300px"  alt="" key={i}/>
                  </Link>
                  <h4 className="text-white">
                    {value.original_title}
                    </h4> 
                    
                  </div>
                ))
              }
        
      </div>

      </main>
        </>

     );
}

export default Upcoming;

Router组件

import React  from "react";
import SignUp from "./Signup";
import Navbar from "./Navbar"
import Login from "./Login";
import Upcoming from "./Upcoming";
import UpcomingDetails from "./UpcomingDetails";
import { 
    Routes,
    Route
} 
from "react-router-dom";

function Router() {
   
    return(
        <>

       <Routes>
                <Route exact path = "/" element = {<> <Navbar/> <Upcoming/> </>}/>
                <Route path = "/cinephile/signup" element={<> <Navbar/> <SignUp/> </>}/>
                <Route path = "/cinephile/login" element={<>   <Login/> </>}/>
                <Route path = "/cinephile/:id" element={<> <Navbar/> <UpcomingDetails/> </>}/>
            </Routes>    
     
        </>
    )
}
export default Router;
问题原因
  • 登录状态login是Login组件内部的局部state,只能作用于当前组件内的Navbar,无法在其他组件间共享。调用setLogin(true)只会更新Login组件内的Navbar,其他组件的Navbar根本获取不到这个状态值。
  • 首页Upcoming组件和Router中渲染的Navbar都没有传入login props,默认值为undefined,因此始终显示未登录状态。
  • 页面跳转后,首页组件重新渲染,但没有任何机制获取登录状态,自然无法同步更新Navbar。
解决方案

推荐两种常用的解决方式,可根据项目规模选择:

方式一:React Context 全局状态共享(适合中大型项目)

1. 创建AuthContext

新建AuthContext.js文件:

import React, { createContext, useState, useContext } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  // 从localStorage读取初始状态,防止页面刷新丢失登录状态
  const [isLoggedIn, setIsLoggedIn] = useState(() => {
    return localStorage.getItem('isLoggedIn') === 'true';
  });

  // 全局登录方法
  const login = () => {
    setIsLoggedIn(true);
    localStorage.setItem('isLoggedIn', 'true');
  };

  // 全局登出方法
  const logout = () => {
    setIsLoggedIn(false);
    localStorage.removeItem('isLoggedIn');
  };

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

// 自定义Hook,简化组件调用
export function useAuth() {
  return useContext(AuthContext);
}

2. 全局包裹AuthProvider

在项目入口文件(如index.js)中,用AuthProvider包裹整个应用:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { AuthProvider } from './AuthContext';
import Router from './Router';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <AuthProvider>
      <Router />
    </AuthProvider>
  </BrowserRouter>
);

3. 修改Navbar组件

通过自定义Hook获取全局登录状态:

import React from "react";
import { Link } from "react-router-dom";
import logo from "../CINEPHILE1.png";
import { useAuth } from "./AuthContext";

function Navbar() {
  const { isLoggedIn } = useAuth();

  return (
    <>
      <nav className="navbar ">
        <div className="container-fluid">
          <Link to="/">
            <img src={logo} alt="" width="100" height="50" />
          </Link>
          <Link
            to="/cinephile/movies"
            className="text-decoration-none text-white "
          >
            MOVIES
          </Link>
          <Link
            to="/cinephile/tv-shows"
            className="text-decoration-none text-white "
          >
            TV SHOWS
          </Link>
          {isLoggedIn ? (
            <Link to="/cinephile/logout" className="text-decoration-none text-white">
              LOG OUT
            </Link>
          ) : (
            <>
              <Link
                to="/cinephile/signup"
                className="text-decoration-none text-white "
              >
                SIGN UP
              </Link>
              <Link
                to="/cinephile/login"
                className="text-decoration-none text-white"
              >
                LOGIN
              </Link>
            </>
          )}
        </div>
      </nav>
    </>
  );
}

export default Navbar;

4. 修改Login组件

调用全局登录方法:

import React, { useState } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import Navbar from "./Navbar";
import { useAuth } from "./AuthContext";

function Login() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const navigate = useNavigate();
  const { login } = useAuth();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const sendData = await axios.post("http://localhost:5000/cinephile/account/login", { email, password });
      if (sendData) {
        login();
        navigate("/");
      }
    } catch (error) {
      toast("Invalid Credentials");
    }
  };

  return (
    <>
      <Navbar />
      <form onSubmit={handleSubmit}>
        <div className="mb-3">
          Email address
          <input type="email" className="form-control" aria-describedby="emailHelp" onChange={(e) => setEmail(e.target.value)} required></input>
        </div>
        <div className="mb-3">
          Password
          <input type="password" className="form-control" onChange={(e) => setPassword(e.target.value)} required></input>
        </div>
        <button type="submit" className="btn btn-primary">LOGIN</button>
      </form>
    </>
  );
}

export default Login;

5. 其他组件调整

所有使用Navbar的组件无需再传递login props,Navbar会自动从Context获取最新状态。

方式二:状态提升到顶层Router组件(适合小型项目)

1. 修改Router组件

将登录状态提升到Router,并提供状态更新方法:

import React, { useState } from "react";
import SignUp from "./Signup";
import Navbar from "./Navbar"
import Login from "./Login";
import Upcoming from "./Upcoming";
import UpcomingDetails from "./UpcomingDetails";
import { Routes, Route } from "react-router-dom";

function Router() {
  const [isLoggedIn, setIsLoggedIn] = useState(() => {
    return localStorage.getItem('isLoggedIn') === 'true';
  });

  const handleLogin = () => {
    setIsLoggedIn(true);
    localStorage.setItem('isLoggedIn', 'true');
  };

  const handleLogout = () => {
    setIsLoggedIn(false);
    localStorage.removeItem('isLoggedIn');
  };

  return(
    <>
      <Routes>
        <Route exact path = "/" element = {<> <Navbar login={isLoggedIn}/> <Upcoming/> </>}/>
        <Route path = "/cinephile/signup" element={<> <Navbar login={isLoggedIn}/> <SignUp/> </>}/>
        <Route path = "/cinephile/login" element={<> <Login onLogin={handleLogin}/> </>}/>
        <Route path = "/cinephile/logout" element={<> <Navbar login={isLoggedIn}/> {/* 实现Logout组件并调用handleLogout */} </>}/>
        <Route path = "/cinephile/:id" element={<> <Navbar login={isLoggedIn}/> <UpcomingDetails/> </>}/>
      </Routes>    
    </>
  )
}
export default Router;

2. 修改Login组件

接收并调用onLogin方法:

function Login({ onLogin }) {
  // ... 原有代码
  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const sendData = await axios.post("http://localhost:5000/cinephile/account/login", { email, password });
      if (sendData) {
        onLogin();
        navigate("/");
      }
    } catch (error) {
      toast("Invalid Credentials");
    }
  };
  // ... 原有代码
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:44:53