Closures
You are experiencing the unupdated state in the console log, because of closures.
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.
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.
in that case, the first render of your component
const addOne = () => {
setCount(count + 1)
console.log(count)
}
is equivalent to (replace count with 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.
In this case, I believe its the closure you are experiencing combined with the asynchronous behavior of setState
Async behaviour
codesandbox
Async behaviour becomes a problem when asynchronous actions are occuring. setTimeout is one of the basic async actions. 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.
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. (or any number of times) and watch how the count result is incorrect, where the countAsync result is correct.
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
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. 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.
All 4 timeouts will fire and simply set count to 0 + 1, hence the result of 1 for the count.