useState加载在异步之前不更新
useState loading not updating before async
我有一个函数 运行Validation 循环遍历一堆数字并进行一些计算。这可能需要一段时间,所以我想包括一个“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
关键字 在函数中并隐式地return一个承诺。
对于您的示例代码和框,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
我有一个函数 运行Validation 循环遍历一堆数字并进行一些计算。这可能需要一段时间,所以我想包括一个“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
关键字 在函数中并隐式地return一个承诺。
对于您的示例代码和框,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