useEffect 中的 useState 不更新状态

Posted

技术标签:

【中文标题】useEffect 中的 useState 不更新状态【英文标题】:useState in useEffect does not update state 【发布时间】: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" />
  );
;

【问题讨论】:

【参考方案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/… !! 这个运算符有什么用?任何有用的链接将不胜感激。 一般称为双重否定运算符。它只会执行(否定)! 两次。第二次它对第一次的结果执行否定。这是一个答案:***.com/questions/10467475/…

以上是关于useEffect 中的 useState 不更新状态的主要内容,如果未能解决你的问题,请参考以下文章

为啥 useState 钩子在 useEffect() 中使用循环时不更新

在 useEffect 中使用 useState 值而不使状态更新 useEffect

反应useState不更新值

我可以在不使用 useState 的情况下访问 useEffect 中的变量吗?

无法获取更新后的状态值 useState 和 UseEffect

HOOK—useState、useEffect的使用