反应如何在登录时动态更改导航栏

react how to change Navbar dynamically when you are logged in

我在 App 组件中有一个导航栏(组件 navbarUser),我想在用户登录时更改导航栏以显示注销和配置文件。 当用户登录时,导航栏不会改变,我在 App 组件和 NavbarUser 中有一个 useEffect,但是如果我进行手动刷新 (F5),那么导航栏更改。 我的代码问题在哪里?动态更改导航栏的解决方案是什么? 我添加了一个视频:video 谢谢。

组件应用程序

import React, { useState, useEffect } from "react";
import logo from "./assets/logoBusca.png";
import "./App.css";
import { Route, Switch } from "wouter";
import logic from "./logic";
import Home from "./components/Home";
import Login from "./components/Login";
import Post from "./components/Post";
import NavbarUser from "./components/NavbarUser";

function App() {
  const [userLogged, setUserLogged] = useState(false);
  useEffect(() => {
    (async () => {
      const loggedIn = await logic.isUserLoggedIn;
      if (loggedIn) setUserLogged(true);
    })();
  }, [userLogged]);
  return (
    <div className="App">
      <header className="App-header">
        <div className="images">
          <div className="logo">
            <a href="/">
              <img src={logo} alt="logo" />
            </a>
          </div>
          <div className="user_flags">
            <NavbarUser />
          </div>
        </div>
      </header>
      <Switch>
        <Route path="/" component={Home} />
        <Route path="/login">{() => <Login />}</Route>
        <Route path="/nuevabusqueda">{() => <Post />}</Route>
      </Switch>
    </div>
  );
}
export default App;

组件导航栏用户

import React, { useState, useEffect } from "react";
import userIcon from "../../assets/userIcon.png";
import logic from "../../logic";

export default function NavbarUser() {
  const [navbarUserIsLogged, setnavbarUserIsLogged] = useState(false);

  useEffect(() => {
    (async () => {
      const loggedIn = await logic.isUserLoggedIn;
      if (loggedIn) setnavbarUserIsLogged(true);
    })();
  }, [navbarUserIsLogged]);

  return (
    <>
      {!navbarUserIsLogged ? (
        <div className="navbar-item has-dropdown is-hoverable">
          <img src={userIcon} alt="user" />
          <div className="navbar-dropdown">
            <a href="/login" className="navbar-item" id="item_login">
              Login
            </a>
            <hr className="navbar-divider" />
            <a href="/registro" className="navbar-item" id="item_register">
              Registro
            </a>
          </div>
        </div>
      ) : (
        <div className="navbar-item has-dropdown is-hoverable">
          <img src={userIcon} alt="user" />
          <div className="navbar-dropdown">
            <a href="/datos" className="navbar-item" id="item_login">
              Perfil
            </a>
            <hr className="navbar-divider" />
            <a href="/user" className="navbar-item" id="item_register">
              Logout
            </a>
          </div>
        </div>
      )}
    </>
  );
}

组件登录

import React, { useState } from "react";
import { useLocation } from "wouter";
import logic from "../../logic";
import "./index.css";

export default function Login() {
  const [messageError, setMessageError] = useState("");
  const [, pushLocation] = useLocation();

  async function handleOnSubmit(e) {
    e.preventDefault();
    const {
      email: { value: email },
      password: { value: password }
    } = e.target;
    e.target.reset();

    try {
      await logic.loginUser(email, password);
      pushLocation("/nuevabusqueda");
    } catch (error) {
      setMessageError(error.message);
    }
  }

  return (
    <>
      {messageError && (
        <div className="message-error">
          <p>{messageError}</p>
        </div>
      )}
      <section className="hero is-fullwidth">
        <div className="hero-body">
          <div className="container">
            <div className="columns is-centered">
              <div className="column is-4">
                <form id="form" onSubmit={e => handleOnSubmit(e)}>
                  <div className="field">
                    <p className="control has-icons-left">
                      <input
                        className="input"
                        name="email"
                        type="email"
                        placeholder="Email"
                        required
                      />
                      <span className="icon is-small is-left">
                        <i className="fas fa-envelope"></i>
                      </span>
                    </p>
                  </div>
                  <div className="field">
                    <p className="control has-icons-left">
                      <input
                        className="input"
                        name="password"
                        type="password"
                        placeholder="Password"
                        required
                      />
                      <span className="icon is-small is-left">
                        <i className="fas fa-lock"></i>
                      </span>
                    </p>
                  </div>
                  <div className="field">
                    <p className="control">
                      <button className="button is-success">Login</button>
                    </p>
                  </div>
                  <div>
                    <a href="/registro">
                      ¿Aún no estás registrado? Acceso para registarte
                    </a>
                  </div>
                </form>
              </div>
            </div>
          </div>
        </div>
      </section>
    </>
  );
}

逻辑

import buscasosApi from "../data";

const logic = {
  set userToken(token) {
    sessionStorage.userToken = token;
  },

  get userToken() {
    if (sessionStorage.userToken === null) return null;
    if (sessionStorage.userToken === undefined) return undefined;
    return sessionStorage.userToken;
  },

  get isUserLoggedIn() {
    return this.userToken;
  },

  loginUser(email, password) {
    return (async () => {
      try {
        const { token } = await buscasosApi.authenticateUser(email, password);
        this.userToken = token;
      } catch (error) {
        throw new Error(error.message);
      }
    })();
  }
};
export default logic;

创建两个组件

<NavLoggedIn>
//Logged in links and styling goes here
<NavLoggedIn>]

<NavPublic>
//Logged out links and styling goes here
<NavPublic/>

最后,在您的 Navbar 组件中,执行以下操作:

const Navbar = () => {
    return (
            <>
                
                        {!isAuth() ? <NavLoggedIn /> : <NavPublic />}
                
        </>
    );
};

拥有全局身份验证上下文,以便您的所有组件都能成功地了解发生的更改 w.r.t 到 login/logout。

目前发生的情况是,即使用户成功登录,NavbarUser 组件也不知道发生了什么,因为您的应用程序中没有全局 state您的 NavbarUser 可以根据此决定是否更改自己的 状态

无论如何,以下语句只是一次性操作,因为 navbarUserIsLoggedNavbarUser 组件的本地内容,并且只会更改 post 组件的第一次渲染,因此此 useEffect本质上不会仅仅基于 navbarUserIsLogged 一次又一次地 运行 因为它永远不会改变。您需要的更改是全局状态更改

 useEffect(() => {
    (async () => {
      const loggedIn = await logic.isUserLoggedIn;
      if (loggedIn) setnavbarUserIsLogged(true);
    })();
  }, [navbarUserIsLogged]);

ReactContextAPI为我们解决了这个问题。想象一下,有一个 上下文或全局状态对象 ,其中一个键是 isAuthenticated

您的 NavbarUser 现在可以订阅该 全局状态 中发生的变化,每当它发生变化时,您的 NavbarUser 组件可以根据它:-

/* Suppose AuthContext is our global Context for giving us the global
 state or context object. We are de-structuring the isAuthenticated key 
from that object here. Suppose the value of isAuthenticated changes in 
global state so that will get informed to our component which uses useContext */

const {isAuthenticated} = useContext(AuthContext)

现在基于此 isAuthenticated 您可以直接为经过身份验证的用户或未经身份验证的用户呈现导航栏。

目前,即使您将相关令牌保存在 localStorage 中,您应用中的组件也不会意识到这些变化的发生。在页面刷新时,localStorage 只是再次读取。

甚至可以通过将 setUserLogged state updater 函数作为 prop 传递给每个子组件并从以便 App 和每个子项都得到重新渲染,但这 不是处理事情的好方法 。在此处使用 Context API

以下答案讨论了 Context API 实现 -

一如既往地参考 React 文档以获取更多参考。在这里分享 useContext 一个 - https://reactjs.org/docs/hooks-reference.html#usecontext