Updating state inside setInterval
in useEffect
can be tricky with functional components. I recently faced a problem where the code kept printing 0, which is the initial value of the state. This is because of the stale closure capturing an outdated variable.
Stale closures occur when a closure captures outdated variables. This can happen when a function is defined inside another function and the inner function captures variables from the outer function. If the outer function returns a new function on each call, the inner function will capture the variables from the call that created it. If the inner function is called later, it will use the outdated variables from the old call.
To fix this issue, you can give the effect a dependency and relaunch the interval every time:
const [idx, setIdx] = useState(0)
useEffect(() => {
const _interval = setInterval(() => {
console.log(idx)
setIdx((idx) => idx + 1)
}, 500)
return clearInterval(_interval)
}, [idx])
This issue can also occur when you are using async events. For example, if you are updating the state, and it takes 5 seconds to update using async
, and a user clicks the button too fast, the events will capture old values and updated values won’t be valid. To fix this, you should use the following form for updating the state: setIdx((idx) => idx + 1)
function someAsyncFunc() {
setTimeout(() => setIdx((idx) => idx + 1), 3000)
}
Top comments (0)