In this very simple click counter example:
const App = () => {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(count 1)
}
return <button onClick={handleClick}>{count}</button>
}
My understanding of the flow is:
- Component gets mounted with
count
=0 - When button is clicked, new
count
state is set with increment of 1. - That triggers a re-render, so now I have
count
=1, and repeats.
However, using the same understanding, why does it not work here?
const App = () => {
const [count, setCount] = useState(0)
useEffect(() => {
const timer = setInterval(() => {
setCount(count 1)
}, 500)
return () => {
clearInterval(timer)
}
}, [])
console.log(count) // this stops at 1, so the timer stops triggering??
return <h1>{count}</h1> // get stuck at 1
}
The outcome of the above code is that the count
gets stuck at value 1. (and weirdly the console.log stops too).
I thought each time the setInterval timer triggers, the count increment will cause a re-render with new count
value and therefore it will just increase by 1 forever?
The fix here is to simply pass in a function argument to access prevState
:
const timer = setInterval(() => {
setCount(oldCount => oldCount 1)
}, 500)
But why couldn't the first approach work? Hope someone can point me to a good article or documentation of this, I tried searching around but couldn't get any explanation.
CodePudding user response:
The first approach doesn't work because with this
setCount(count 1)
You are creating a copy of the count value at that particular time you created the callback. This means that every 500ms you will re-execute this line
setCount(0 1)
That won't cause a re-render because react is intelligent enough to understand that you are passing the same value to the setCount function so the re-render would not be necessary.
However by passing a callback to setCount:
setCount(oldCount => oldCount 1)
You are saying that you want the current value of the state count, so each time the argument of that function will be different and therefore it will cause a re-render.
You can find a doc about this topic here: https://reactjs.org/docs/hooks-reference.html#functional-updates