【问题标题】:React useContext doesn't update the child component, but works on page refreshReact useContext 不会更新子组件,但适用于页面刷新
【发布时间】:2021-06-12 01:34:14
【问题描述】:

我有一个带有子菜单的导航栏组件。登录后,导航栏的子菜单应更改。 我使用了钩子useContext,但是当用户登录时它不会刷新导航栏组件。当我刷新页面时它工作正常。 我的代码问题在哪里?

应用组件

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

function App() {
  const [user, setUser] = useState(false);

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

  return (
    <AuthContext.Provider value={user}>
      <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>
        <Route path="/">
          <Home />
        </Route>
        <Route path="/login">
          <Login />
        </Route>
        <Route path="/nuevabusqueda">
          <Post />
        </Route>
      </div>
    </AuthContext.Provider>
  );
}

export default App;

导航栏组件

import React, { useContext } from "react";
import userIcon from "../../assets/userIcon.png";
import { AuthContext } from "../../context/AuthContext";

export default function NavbarUser() {
  const isAuthenticated = useContext(AuthContext);

  return (
    <>
      {!isAuthenticated ? (
        <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 { createContext } from "react";

export const AuthContext = createContext();

逻辑组件

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;

【问题讨论】:

    标签: reactjs use-context


    【解决方案1】:

    我认为 Login 组件不会调用 setUser(true)

    这是一个如何工作的示例。

    const { useState, useEffect, createContext, useContext } = React;
    
    const AuthContext = createContext();
    
    const Login = () => {
      const [isAuthenticated, setAuth] = useContext(AuthContext);
      const onClick = () => setAuth(true);
      return <button disabled={isAuthenticated} onClick={onClick}>Login</button>
    }
    
    const App = () => {
      const [isAuthenticated] = useContext(AuthContext);
      
      return <div>
        <Login/>
        <button disabled={!isAuthenticated}>{isAuthenticated ? "Authenticated" : "Not Authenticated"}</button>
      </div>;
    }
    
    const AuthProvider = ({children}) => {
      const [isAuthenticated, setAuth] = useState(false);
      
      return <AuthContext.Provider value={[isAuthenticated, setAuth]}>
        {children}
      </AuthContext.Provider>;
    }
    
    ReactDOM.render(
        <AuthProvider>
          <App />
        </AuthProvider>,
        document.getElementById('root')
      );
    <script src="https://unpkg.com/react/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    <div id="root"></div>

    【讨论】:

      【解决方案2】:

      在我看来,您没有更新状态。最初您的状态是false。第一次渲染后,你的效果被触发,状态再次设置为false。之后你的效果不再运行,因为它取决于它应该改变的状态。没有状态改变 - 没有效果 - 没有状态再次改变。另外,如果状态发生变化,它会触发效果,但你不需要这样做。

      您的目标是构建一个系统:

      1. 检查当前负载状态
      2. 更改时更新(或重新检查)状态

      为此,您需要某种方式从logic 异步发送消息以做出反应。您可以通过某种订阅来做到这一点,如下所示:

      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;
              this.notify(true);
            } catch (error) {
              throw new Error(error.message);
            }
          })();
        },
        subscribers: new Set(),
        subscribe(fn) {
          this.subscribers.add(fn);
          return () => {
            this.subscribers.remove(fn);
          };
        },
        notify(status) {
          this.subscribers.forEach((fn) => fn(status));
        },
      };
      
      function useAuthStatus() {
        let [state, setState] = useState("checking");
        let setStatus = useCallback(
          (status) => setState(status ? "authenticated" : "not_authenticated"),
          [setState]
        );
        useEffect(function () {
          return logic.subscribe(setStatus);
        }, []);
      
        useEffect(function () {
          setStatus(logic.isUserLoggedIn);
        }, []);
      
        return state;
      }
      

      请注意,现在有三种可能的状态 - 'checking'、'authenticated' 和 'not_authenticated'。它更详细,可以防止一些错误。例如,如果您想在用户未通过身份验证时将其重定向到登录页面。

      【讨论】:

        猜你喜欢
        • 2021-03-18
        • 2019-06-21
        • 1970-01-01
        • 1970-01-01
        • 2020-08-28
        • 1970-01-01
        • 1970-01-01
        • 2020-12-08
        • 1970-01-01
        相关资源
        最近更新 更多