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)
.
最后,!!null
是 false
而 !!
是 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 的情况下访问 useEffect 中的变量吗?