reactjs - useState 在异步之前加载不更新
问题描述
我有一个函数 runValidation 遍历一堆数字并进行一些计算。这可能需要一段时间,所以我想包含一个“isLoading”useState。
所以我做了一个简单的
const [isLoading, setIsLoading] = useState(false)
当我单击一个按钮时,我想将 isLoading 设置为 true,运行我的函数,然后执行 setIsLoading(false)。所以我使我的验证功能异步,以及按钮处理程序
const handleClick = async () => {
setIsLoading(true)
const isValid = await runValidation()
setIsLoading(false)
}
我的按钮组件很简单
<button onClick={() => handleClick()}>Run Validations</button>
但是,每次我尝试运行它时,在验证功能完成之前都不会设置加载。因此,在“isLoading”变量上使用 useEffect。我得到 console.logs 显示
Running Validations
isLoading: true
isLoading: false
为什么我的“isLoading”在异步之后才为真?有没有办法确定?我尝试将 setIsLoading(true) 向下移动到 onClick 按钮 - 我尝试使用 .then() 而不是 await 等 - 但 isLoading 仅在异步函数之后设置为 true。
编辑:示例 - https://codesandbox.io/s/suspicious-cartwright-0y5jk
解决方案
仅仅因为你声明了一个函数async
并不意味着它实际上是一个异步函数,它只是允许你在await
函数中使用关键字并隐式返回一个 Promise。
在您的示例代码框的情况下,runValidation
已声明async
但运行完全同步的代码。
const runValidation = async () => {
let result = 0;
console.log("Running Validation");
for (let i = 1; i <= 500000; i++) {
if (i > 200000) {
result = i;
break;
}
}
return result;
};
如果您实际上是异步执行的,那么您会注意到控制台日志是相同的。
例子:
const runValidation = async () => {
console.log("Running Validation");
const result = await new Promise((resolve) => {
setTimeout(() => {
let result = 0;
for (let i = 1; i <= 500000; i++) {
if (i > 200000) {
result = i;
break;
}
}
resolve(result);
}, 3000);
});
return result;
};
Running Validation // logged immediately in callback isLoading true // logged in useEffect next render cycle isLoading false // logged in useEffect ~3 seconds later
为什么日志输出一样?
const handleClick = async () => { setIsLoading(true); await runValidation(); setIsLoading(false); };
在这里,您已将状态更新排入队列,然后立即调用runValidation
,这将在控制台记录"Running Validation"
,然后"isLoading true"
从useEffect
. 等待隐式 Promise(时间不长),然后将另一个状态更新排入队列并"isLoading false"
从useEffect
.
您的代码似乎完全按照我的预期工作。
如果您将验证日志移动到异步逻辑中,那么控制台日志输出可能更像您的预期,因为您现在让 React 状态有机会在“异步”代码完成“触发”之前更新和重新呈现。
const runValidation = async () => {
const result = await new Promise((resolve) => {
setTimeout(() => {
console.log("Running Validation");
let result = 0;
for (let i = 1; i <= 500000; i++) {
if (i > 200000) {
result = i;
break;
}
}
resolve(result);
}, 3000);
});
return result;
};
输出
isLoading true Running Validation isLoading false
推荐阅读
- antlr4 - 至少一个 Antlr 规则可选部分
- javascript - 如何使用 JS/jQuery 通过 PHP 代理向 API 发送请求
- laravel - 使用 sanctum 的 SPA 身份验证失败,并显示以下错误消息:“未经身份验证”。
- java - 期待 IDENT,找到 ':'
- python - 在 Jupyter 砍属性
- javascript - 单击图库中的缩略图时显示 Swiper 滑块
- swift - 如何改变从其他视图传递的变量
- encryption - PGP加密说明
- python - ModuleNotFoundError:使用自然语言时没有名为“google”的模块
- parsing - 使用 Pest.rs,有没有办法将评论存储为令牌?