1

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

组件应用

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;

4

2 回答 2

3

拥有一个全局身份验证上下文,以便您的所有组件都可以成功地了解登录/注销发生的更改。

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

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

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

React Context API 为我们解决了这个问题。想象一下有一个上下文或全局状态对象,其中一个键是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 状态更新器函数作为道具传递给每个子组件并触发它们的更新来使您当前的实现工作,这样每个子组件App都会重新渲染,但这不是一个好方法关于事情。在这里使用Context API

以下答案讨论了上下文 API实现 - React 上下文在首次登录时返回未定义,但适用于页面刷新

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

于 2021-02-28T06:18:32.570 回答
0

Create two components

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

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

Finally, in your Navbar component, do this:

const Navbar = () => {
    return (
            <>
                
                        {!isAuth() ? <NavLoggedIn /> : <NavPublic />}
                
        </>
    );
};
于 2021-02-26T10:31:43.563 回答