I have a function react component that has a counter that starts from 10000 and goes to 0.
I am setting a setInterval callback using useEffect hook during component mounting. The callback then updates the counter state.
But I don't know why, the count value never decreases. Each time the callback runs count is 10000.
(I am using react & react-dom version 16.8.3)
Function component is as below:
import React, { useState, useEffect, useRef } from 'react'
const Counter = () => {
const timerID = useRef()
let [count, setCount] = useState(10000)
useEffect(() => {
timerID.current = setInterval(() => {
//count here is always 10000
if (count - 1 < 0) {
setCount(0)
} else {
setCount(count - 1)
}
}, 1)
}, [])
return <h1 className="counter">{count}</h1>
}
export default Counter
Here is the link to codesandbox: link
You need to watch for changes in count, and also clean up your useEffect():
useEffect(() => {
timerID.current = setInterval(() => {
if (count - 1 < 0) {
setCount(0)
} else {
setCount(count - 1)
}
}, 100)
return () => clearInterval(timerID.current);
}, [count])
As @Pavel mentioned, Dan Abramov explains why here.
There are 2 options:
1) Include count in the dependencies
This is not ideal, as it means a new setInterval will be created on every change of count, so you would need to clean it up on every render, example:
useEffect(() => {
timerID.current = setInterval(() => {
//count here is always 10000
if (count - 1 < 0) {
setCount(0)
} else {
setCount(count - 1)
}
}, 1)
return () => clearInterval(timerID.current) // Added this line
}, [count]) // Added count here
2) Add the count in the setInterval callback function.
This is the best approach for intervals, as it avoids, setting new ones all the time.
useEffect(() => {
timerID.current = setInterval(() => {
// count is used inside the setCount callback and has latest value
setCount(count => {
if (count - 1 < 0) { // Logic moved inside the function, so no dependencies
if (timerID.current) clearInterval(timerID.current)
return 0
}
return count - 1
})
}, 1)
return () => {
if (timerID.current) clearInterval(timerID.current) // Makes sure that the interval is cleared on change or unmount
}
}, [])
Here is the sandbox link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With