[英]REACT - Updating state and then doing a console.log , shows unupdated state
After clicking the button the console shows 0 and the page 1单击按钮后,控制台显示 0 和页面 1
function App() {
const [count, setCount] = useState(0);
const addOne = () => {
setCount(count + 1)
console.log(count)
}
return (
<>
<p>{count}</p>
<button onClick={addOne}>Add</button>
</>
);
}
I think is because the setCount()
is happening asynchronously but even if I add a setTimeout to the console.log()
, the console keeps showing the unupdated state我认为是因为setCount()
是异步发生的,但即使我将 setTimeout 添加到console.log()
,控制台也会继续显示未更新的 state
Why???为什么???
The state updation in React is always asynchronous. React 中的 state 更新始终是异步的。 you will find the updated state value of count in useEffect您会在 useEffect 中找到更新后的 state 计数值
function App() {
const [count, setCount] = useState(0);
useEffect(()=> {
console.log('count',count);
},[count])
const addOne = () => {
setCount(count + 1)
}
return (
<>
<p>{count}</p>
<button onClick={addOne}>Add</button>
</>
);
}
Closures闭包
You are experiencing the unupdated state in the console log, because of closures .由于关闭,您在控制台日志中遇到未更新的 state 。
when your function is created when the component is rendered, and closure is created with the value of count at the time the closure is created.当您的 function 在渲染组件时创建时,并且在创建闭包时使用 count 的值创建闭包。
if the value of count is 0, and your component rerenders, a closure of your function will be created and attached to the event listener of the onlcick.如果 count 的值为 0,并且您的组件重新呈现,则会创建 function 的闭包并将其附加到 onlcick 的事件侦听器。
in that case, the first render of your component在这种情况下,您的组件的第一次渲染
const addOne = () => {
setCount(count + 1)
console.log(count)
}
is equivalent to (replace count with 0)相当于(将计数替换为 0)
const addOne = () => {
setCount(0 + 1)
console.log(0)
}
therefore it makes sense in your case that count is 0 when it is console logged.因此,在您的情况下,控制台记录时计数为 0 是有意义的。
In this case, I believe its the closure you are experiencing combined with the asynchronous behavior of setState在这种情况下,我相信您遇到的关闭与 setState 的异步行为相结合
Async behaviour异步行为
Async behaviour becomes a problem when asynchronous actions are occuring.当异步操作发生时,异步行为会成为一个问题。 setTimeout is one of the basic async actions. setTimeout 是基本的异步操作之一。 Async actions always require that you provide a function to the setCount function, which will accept the latest state as a parameter, with the nextState being the return value of this function. Async actions always require that you provide a function to the setCount function, which will accept the latest state as a parameter, with the nextState being the return value of this function. This will always ensure the current state is used to calculate the next state, regardless of when it is executed asynchronously.这将始终确保当前 state 用于计算下一个 state,无论何时异步执行。
const addOneAsync = () => {
setCountAsync((currentState) => {
const nextState = currentState + 1;
console.log(`nextState async ${nextState}`);
return nextState;
});
};
I have created a codesandbox demonstrating the importance of this.我创建了一个代码框来展示这一点的重要性。 CLick the "Count" button fast 4 times.快速单击“计数”按钮 4 次。 (or any number of times) and watch how the count result is incorrect, where the countAsync result is correct. (或任意次数)并观察计数结果如何不正确,countAsync 结果在哪里正确。
addOneAsync: when the button is clicked, a closure is created around addOneAsync
, but since we are using a function which accepts the currentState, when it eventually fires, the current state will be used to calculate the next state addOneAsync:单击按钮时,会在addOneAsync
周围创建一个闭包,但由于我们使用的是接受 currentState 的 function,因此当它最终触发时,当前的 state 将用于计算下一个 state
addOne: When the button is clicked, a closure is created around addOne
where count is captured as the value at the time of the click. addOne:单击按钮时,会在addOne
周围创建一个闭包,其中 count 被捕获为单击时的值。 If you click the count button 4 times before count has increased, you will have 4 closures of addOne set to be fired, where count is captured as 0.如果在 count 增加之前单击 count 按钮 4 次,您将触发 4 个 addOne 闭包,其中 count 被捕获为 0。
All 4 timeouts will fire and simply set count to 0 + 1, hence the result of 1 for the count.所有 4 次超时都将触发并简单地将计数设置为 0 + 1,因此计数的结果为 1。
Yes, you're right about the origins of this behavior and the other posters here seem to have explained how to fix it.是的,您对这种行为的起源是正确的,这里的其他海报似乎已经解释了如何解决它。 However, I don't see the answer to your specific question:但是,我看不到您的具体问题的答案:
...but even if I add a setTimeout to the console.log(), the console keeps showing the unupdated state Why??? ...但是即使我在 console.log() 中添加了 setTimeout,控制台仍然显示未更新的 state 为什么?
So what you mean is that even if you handle that console.log call like so:所以你的意思是,即使你像这样处理 console.log 调用:
const addOne = () => {
setCount((count) => count + 1);
setTimeout(() => console.log(count), 1000);
}
It will STILL print the old, un-updated value of count
.它仍然会打印旧的、未更新的count
值。 Why?为什么? Shouldn't the timeout allow time for count
to update?超时不应该允许count
更新吗? I will quote the answer:我将引用答案:
This is subtle but expected behavior.这是微妙但预期的行为。 When setTimeout is scheduled it's using the value of
count
at the time it was scheduled.安排 setTimeout 时,它使用安排时的count
。 It's relying on a closure to access count asynchronously.它依靠闭包来异步访问计数。 When the component re-renders a new closure is created but that doesn't change the value that was initially closed over.当组件重新渲染时,会创建一个新的闭包,但这不会改变最初关闭的值。
Source: https://github.com/facebook/react/issues/14010#issuecomment-433788147来源: https://github.com/facebook/react/issues/14010#issuecomment-433788147
So there you have it.所以你有它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.