setState 不是异步的
Published on 6th April 2024
丢弃所有已经消化的信息,让问题的核心浮出水面。 - 田正仁《思考力》
“设置状态是异步的”,这句话有点误导,人们口中的“异步”,通常是指状态设置的调用附近的代码,无法看到真正更新的值。事实上,设置状态并非异步,一旦给 setState
加上 await
,即可验证:
function App() {
const [count, setCount] = useState(0);
const asyncIncrement = async () => {
console.log('before: ', count);
await setCount(count + 1);
console.log('after: ', count);
// before 和 after 的 count 一样
};
return (
<>
<button onClick={asyncIncrement}>async increment</button>
</>
);
}
你可以前往此处尝试。
无法看到更新的值,真正原因是因为捕获到的值是过时的闭包。以下代码可以简单的表明这点:
function createIncrement(initialValue) {
let value = initialValue;
function increment() {
value += 1;
console.log('incremented: ', value);
}
let message = `Current value is ${value}`;
function log() {
console.log(message);
}
return [log, increment];
}
const [log, increment] = createIncrement(0);
increment(); // logs 1
increment(); // logs 2
increment(); // logs 3
increment(); // logs 4
// ...
log(); // Current value is 0
increment() 调用了 n 次,value 也增加了 n,但 log() 是一个过时的闭包,它捕获到的是 message 这个变量,它永远输出 Current value is 0。