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

如何实现App组件响应Navbar组件的用户登录状态变更?

问题描述

我现在有一个React项目,App.js和Navbar组件的代码如下,我的设计思路是页面渲染Navbar组件,当用户已登录时渲染Questions组件,未登录时渲染其他内容,但现在App组件无法获取Navbar里变更的用户状态,不知道怎么实现状态同步。

App.js代码

import React, {Component} from 'react'; 
import logo from './logo.svg'; 
import './App.css'; 
import {BrowserRouter as Router} from 'react-router-dom'; 
import Navbar from "./components/nav"; 
import firebase from './firebase'; 
import Questions from './components/questions'; 
import Stuff from './components/stuff'; 

class App extends Component { 
  constructor(p) { 
    super(p); 
    this.state = {user: null} 
  } 

  render() { 
    return ( 
      <div className="App"> 
        <Navbar/> 
        {this.state.user ? <Questions/> : <Stuff/>} 
      </div> 
    ); 
  } 
} 

export default App;
import React from 'react'; 
import {Navbar as Bar, Nav, NavItem, Modal, Button, FormControl} from 'react-bootstrap'; 
import {BrowserRouter as Router, Link, Route} from 'react-router-dom'; 
import firebase, {auth} from '../firebase'; 

class Navbar extends React.Component { 
  constructor(props) { 
    super(props); 
    this.state = {}; 
    this.login = this.login.bind(this); 
    this.logout = this.logout.bind(this); 
    this.openLogin = this.openLogin.bind(this); 
    this.handleClose = this.handleClose.bind(this); 
  } 

  componentDidMount() { 
    auth.onAuthStateChanged((user) => { 
      if (user) { 
        this.setState({user}); 
      } 
    }); 
  } 

  logout() { 
    auth.signOut() 
      .then(() => { 
        this.setState({ user: null }); 
      }); 
  } 

  login() { 
    var email = document.getElementById('email').value; 
    var password = document.getElementById('password').value; 
    auth.signInWithEmailAndPassword(email, password) 
      .then(result => { 
        const user = result.user; 
        this.setState({user}); 
        document.getElementById('close').click(); 
      } ).catch(e => console.log(e)); 
  } 

  openLogin() { 
    this.setState({show: true}); 
  } 

  handleClose() { 
    this.setState({show: false}); 
  } 

  render() { 
    return ( 
      <React.Fragment> 
        <Router> 
          <Bar> 
            <Bar.Header> 
              <Bar.Brand> 
                <a href="/home">UczIchApp</a> 
              </Bar.Brand> 
            </Bar.Header> 
            <Nav> 
              { this.state.user ? <NavItem onClick={this.logout}>Wyloguj się</NavItem> : <NavItem onClick={this.openLogin}>Zaloguj się</NavItem> } 
            </Nav> 
          </Bar> 
        </Router> 
        <Modal show={this.state.show} onHide={this.handleClose}> 
          <Modal.Header closeButton> 
            <Modal.Title>Modal heading</Modal.Title> 
          </Modal.Header> 
          <Modal.Body> 
            <form> 
              <FormControl id="email" type="email" label="Email address" placeholder="Enter email" /> 
              <FormControl id="password" label="Password" type="password"/> 
              <Button onClick={this.login}>Zaloguj</Button> 
            </form> 
          </Modal.Body> 
          <Modal.Footer> 
            <Button id="close" onClick={this.handleClose}>Close</Button> 
          </Modal.Footer> 
        </Modal> 
      </React.Fragment> 
    ) 
  } 
} 

export default Navbar;
解决方案

嘿,这个问题其实很常见——你现在把用户状态分别存在了Navbar和App两个组件里,导致它们的状态不同步。咱们可以用几种方法解决,下面给你讲两个最实用的方案:

方法一:状态提升(最直接的小项目方案)

核心思路是把用户状态和登录/登出逻辑都移到App组件里,让App作为状态的唯一管理者,Navbar只负责接收props并触发动作,这样两个组件就能共享同一套用户状态了。

步骤1:修改App.js,接管状态和逻辑

import React, {Component} from 'react'; 
import logo from './logo.svg'; 
import './App.css'; 
import {BrowserRouter as Router} from 'react-router-dom'; 
import Navbar from "./components/nav"; 
import firebase, {auth} from './firebase'; // 别忘了导入auth
import Questions from './components/questions'; 
import Stuff from './components/stuff'; 

class App extends Component { 
  constructor(p) { 
    super(p); 
    this.state = {
      user: null,
      showLoginModal: false // 把弹窗状态也移到这里
    };
    // 绑定方法到当前实例
    this.login = this.login.bind(this);
    this.logout = this.logout.bind(this);
    this.openLogin = this.openLogin.bind(this);
    this.handleClose = this.handleClose.bind(this);
  } 

  componentDidMount() { 
    // 把Navbar里的auth监听移到App,统一监听用户状态变化
    auth.onAuthStateChanged((user) => { 
      this.setState({user}); 
    }); 
  }

  logout() { 
    auth.signOut() 
      .then(() => { 
        this.setState({ user: null }); 
      }); 
  } 

  login() { 
    const email = document.getElementById('email').value; 
    const password = document.getElementById('password').value; 
    auth.signInWithEmailAndPassword(email, password) 
      .then(result => { 
        this.setState({user: result.user}); 
        this.handleClose(); // 登录成功后关闭弹窗
      }).catch(e => console.log(e)); 
  }

  openLogin() { 
    this.setState({showLoginModal: true}); 
  } 

  handleClose() { 
    this.setState({showLoginModal: false}); 
  }

  render() { 
    return ( 
      <div className="App"> 
        {/* 把需要的状态和方法通过props传给Navbar */}
        <Navbar 
          user={this.state.user}
          logout={this.logout}
          openLogin={this.openLogin}
          showLoginModal={this.state.showLoginModal}
          handleClose={this.handleClose}
          login={this.login}
        /> 
        {this.state.user ? <Questions/> : <Stuff/>} 
      </div> 
    ); 
  } 
} 

export default App;

步骤2:修改Navbar.js,用props替代自身状态

去掉Navbar里的state和auth逻辑,全部依赖App传递的props:

import React from 'react'; 
import {Navbar as Bar, Nav, NavItem, Modal, Button, FormControl} from 'react-bootstrap'; 
import {BrowserRouter as Router} from 'react-router-dom'; 

class Navbar extends React.Component { 
  render() { 
    // 解构props,让代码更简洁
    const { user, logout, openLogin, showLoginModal, handleClose, login } = this.props;
    return ( 
      <React.Fragment> 
        <Router> 
          <Bar> 
            <Bar.Header> 
              <Bar.Brand> 
                <a href="/home">UczIchApp</a> 
              </Bar.Brand> 
            </Bar.Header> 
            <Nav> 
              {/* 直接用props里的user状态判断显示登录/登出按钮 */}
              { user ? <NavItem onClick={logout}>Wyloguj się</NavItem> : <NavItem onClick={openLogin}>Zaloguj się</NavItem> } 
            </Nav> 
          </Bar> 
        </Router> 
        {/* 弹窗的显示状态也用props里的值 */}
        <Modal show={showLoginModal} onHide={handleClose}> 
          <Modal.Header closeButton> 
            <Modal.Title>Modal heading</Modal.Title> 
          </Modal.Header> 
          <Modal.Body> 
            <form> 
              <FormControl id="email" type="email" label="Email address" placeholder="Enter email" /> 
              <FormControl id="password" label="Password" type="password"/> 
              <Button onClick={login}>Zaloguj</Button> 
            </form> 
          </Modal.Body> 
          <Modal.Footer> 
            <Button onClick={handleClose}>Close</Button> 
          </Modal.Footer> 
        </Modal> 
      </React.Fragment> 
    ) 
  } 
} 

export default Navbar;

方法二:使用Context API(适合中大型项目)

如果以后你的项目组件层级变深,props层层传递会很麻烦,推荐用React的Context API来全局共享用户状态,这样任何组件都能直接获取用户信息,不用再手动传props。

步骤1:创建AuthContext

新建一个AuthContext.js文件,封装用户状态和相关方法:

import React, { createContext, useState, useEffect } from 'react';
import { auth } from './firebase';

// 创建Context
export const AuthContext = createContext();

// 创建Provider组件,包裹需要共享状态的组件
export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 监听用户状态变化,组件卸载时清理订阅
    const unsubscribe = auth.onAuthStateChanged(user => {
      setUser(user);
    });
    return unsubscribe;
  }, []);

  // 封装登录方法
  const login = (email, password) => {
    return auth.signInWithEmailAndPassword(email, password);
  };

  // 封装登出方法
  const logout = () => {
    return auth.signOut();
  };

  // 把状态和方法传给所有子组件
  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

步骤2:用AuthProvider包裹App

修改index.js(或者App的父组件),让整个App都能访问Context里的状态:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { AuthProvider } from './AuthContext';

ReactDOM.render(
  <AuthProvider>
    <App />
  </AuthProvider>,
  document.getElementById('root')
);

步骤3:在App和Navbar里使用Context

修改App.js,用hooks获取用户状态:

import React, { useContext } from 'react'; 
import logo from './logo.svg'; 
import './App.css'; 
import {BrowserRouter as Router} from 'react-router-dom'; 
import Navbar from "./components/nav"; 
import Questions from './components/questions'; 
import Stuff from './components/stuff'; 
import { AuthContext } from './AuthContext';

// 改成函数组件更方便使用hooks
function App() { 
  const { user } = useContext(AuthContext);

  return ( 
    <div className="App"> 
      <Navbar/> 
      {user ? <Questions/> : <Stuff/>} 
    </div> 
  ); 
} 

export default App;

修改Navbar.js,同样用hooks获取Context里的状态和方法:

import React, { useContext, useState } from 'react'; 
import {Navbar as Bar, Nav, NavItem, Modal, Button, FormControl} from 'react-bootstrap'; 
import {BrowserRouter as Router} from 'react-router-dom'; 
import { AuthContext } from '../AuthContext'; 

function Navbar() { 
  const { user, login, logout } = useContext(AuthContext);
  // 弹窗状态还是放在Navbar自己的state里,因为只有它用得到
  const [showLoginModal, setShowLoginModal] = useState(false);

  const openLogin = () => setShowLoginModal(true);
  const handleClose = () => setShowLoginModal(false);

  const handleLogin = () => {
    const email = document.getElementById('email').value;
    const password = document.getElementById('password').value;
    login(email, password)
      .then(() => {
        handleClose(); // 登录成功后关闭弹窗
      })
      .catch(e => console.log(e));
  };

  return ( 
    <React.Fragment> 
      <Router> 
        <Bar> 
          <Bar.Header> 
            <Bar.Brand> 
              <a href="/home">UczIchApp</a> 
            </Bar.Brand> 
          </Bar.Header> 
          <Nav> 
            { user ? <NavItem onClick={logout}>Wyloguj się</NavItem> : <NavItem onClick={openLogin}>Zaloguj się</NavItem> } 
          </Nav> 
        </Bar> 
      </Router> 
      <Modal show={showLoginModal} onHide={handleClose}> 
        <Modal.Header closeButton> 
          <Modal.Title>Modal heading</Modal.Title> 
        </Modal.Header> 
        <Modal.Body> 
          <form> 
            <FormControl id="email" type="email" label="Email address" placeholder="Enter email" /> 
            <FormControl id="password" label="Password" type="password"/> 
            <Button onClick={handleLogin}>Zaloguj</Button> 
          </form> 
        </Modal.Body> 
        <Modal.Footer> 
          <Button onClick={handleClose}>Close</Button> 
        </Modal.Footer> 
      </Modal> 
    </React.Fragment> 
  ) 
} 

export default Navbar;

额外小建议

  • 尽量避免用document.getElementById获取输入值,推荐用React的受控组件(把输入值存在组件state里),这样更符合React的单向数据流,也更容易维护。
  • 记得在组件卸载时清理firebase的auth订阅(比如方法二里的return unsubscribe),避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:54:12