闭包
由于关闭,您在控制台日志中遇到未更新状态。
在渲染组件时创建函数,并在创建闭包时使用 count 的值创建闭包。
如果 count 的值为 0,并且您的组件重新呈现,则将创建函数的闭包并将其附加到 onlcick 的事件侦听器。
在这种情况下,您的组件的第一次渲染
const addOne = () => {
setCount(count + 1)
console.log(count)
}
相当于(将计数替换为 0)
const addOne = () => {
setCount(0 + 1)
console.log(0)
}
因此,在您的情况下,控制台记录时计数为 0 是有意义的。
在这种情况下,我相信您遇到的关闭与 setState 的异步行为相结合
异步行为
密码箱
当异步操作发生时,异步行为会成为一个问题。setTimeout 是基本的异步操作之一。异步操作始终要求您向 setCount 函数提供一个函数,该函数将接受最新状态作为参数,nextState 是此函数的返回值。这将始终确保使用当前状态来计算下一个状态,无论何时异步执行。
const addOneAsync = () => {
setCountAsync((currentState) => {
const nextState = currentState + 1;
console.log(`nextState async ${nextState}`);
return nextState;
});
};
我创建了一个代码框来展示这一点的重要性。快速单击“计数”按钮 4 次。(或任意次数)并观察计数结果如何不正确,countAsync 结果在哪里正确。
addOneAsync:当按钮被点击时,会在 周围创建一个闭包addOneAsync
,但是由于我们使用的是一个接受 currentState 的函数,所以当它最终触发时,当前状态将用于计算下一个状态
addOne
addOne:当单击按钮时,会在 count 被捕获为单击时的值的位置周围创建一个闭包。如果在 count 增加之前单击 count 按钮 4 次,您将触发 4 个 addOne 闭包,其中 count 被捕获为 0。
所有 4 次超时都将触发并简单地将计数设置为 0 + 1,因此计数的结果为 1。