Back

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