【问题标题】:useState in useEffect does not update stateuseEffect 中的 useState 不更新状态
【发布时间】:2021-01-19 07:57:57
【问题描述】:

我是 React Hooks 的新手并使用 react 16.13.1。

我将实现Auth 组件,它可以处理登录。

但它似乎没有正确更新状态currentUser,即使setCurrentUser 是用响应对象调用的。

这段代码有什么问题?

import React, { useState, useEffect } from "react";
import { Route, Redirect } from "react-router-dom";
import { checkLoggedIn } from "utils/Api";

export const Auth = (props) => {
  const [currentUser, setCurrentUser] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  console.log(currentUser);

  useEffect(() => {
    const f = async () => {
      setIsLoading(true);
      console.log(isLoading);
      const res = await checkLoggedIn();
      if (!!res) { // ==> true
        setCurrentUser(res);
        console.log(currentUser); // ==> null!
      }
      setIsLoading(false);
    };
    f();
  });

  if (isLoading) {
    return <div>loading</div>;
  }

  console.log(currentUser); // ==> null!
  return !!currentUser ? (
    <Route children={props.children} />
  ) : (
    <Redirect to={"/login"} />
  );
};

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    setCurrentUser 异步更新状态,所以你不能在之后立即使用currentUser

    但是,您可以使用另一个useEffect 来了解状态何时更改:

    useEffect(() => {
      // currentUser changed
    }, [currentUser])
    

    我还注意到您没有将空数组传递给您已经拥有的useEffect,因此它会在每次更新组件时触发。如果您只需要在必须传递一个空数组作为第二个参数时执行 useEffect,如下所示:

      useEffect(() => {
        const f = async () => {
          // ...
        };
        f();
      }, []);
    

    【讨论】:

      【解决方案2】:

      由于 React 中设置状态的异步性质,调用状态设置器 (setCurrentUser) 可能会出现问题。当我使用 React 时,我还没有找到任何明确的答案是它与 setState 异步还是同步。在我看来,在这种复杂情况下最好的解决方案就是使用类。它会给你很多无法用钩子复现的生命周期方法,在auth组件中会更合适。 React Hooks 并不是在所有情况下都可以替代类,它只是一种工具,可以让您在只需要进行简单改进的同时不破坏现有代码。

      【讨论】:

      • 另外,对于我来说,使用 useEffect 的代码看起来不像 componentDidUpdate 和 componentWillReceiveProps 那样可读
      【解决方案3】:

      感谢@germanescobar 和@Vadim 教了我很多东西!

      而且我现在注意到useState 的论点与这个问题有关。

      变化

      const [currentUser, setCurrentUser] = useState(null);
      

      进入

      const [currentUser, setCurrentUser] = useState({});
      

      好像解决了不更新的问题(不过我不知道是什么原因)。

      【讨论】:

      • 将初始状态与稍后分配给它的类型相同是一种很好的做法。很难说这是否真的解决了问题。我认为最好坚持使用@germanescobar 解决方案或使用类
      • 我也使用了germanescobar的解决方案,但我也不得不将初始状态更改为{}。
      【解决方案4】:

      正如@germanescobar 已经回答setCurrentUser 异步更新状态。因此,您需要使用仅包含 currentUser 的依赖列表 useEffect 来获取最新的状态值。

      但是,对于您的用例,您只关心currentUser 是否真实,并且您希望将用户重定向到所需页面。因此,您不需要另一个 useEffect,因为您没有基于 currentUser 执行任何其他副作用。

      在实际场景中,您希望显示加载/启动屏幕,直到您能够决定用户是否通过身份验证。因此,您应该在异步任务完成后将isLoading 的初始值更改为true,并将状态更改为false。我建议您将州名称重命名为:

      const [shouldCheckLogin, setShouldCheckLogin] = useState(true).

      最后,!!nullfalse!!{}true。对于object 的值,可以使用null 对其进行初始化。

      console.log(!!null)
      console.log(!!{})

      【讨论】:

      • 这是 Dan Abramov 介绍 hooks 的一篇很棒的文章:medium.com/@dan_abramov/…
      • !! 这个运算符有什么用?任何有用的链接将不胜感激。
      • 一般称为双重否定运算符。它只会执行(否定)! 两次。第二次它对第一次的结果执行否定。这是一个答案:stackoverflow.com/questions/10467475/…
      猜你喜欢
      • 2020-10-20
      • 2021-01-17
      • 2022-01-15
      • 1970-01-01
      • 2020-10-16
      • 2021-01-27
      • 1970-01-01
      • 2023-02-26
      • 2020-04-08
      相关资源
      最近更新 更多