I wanted to start a timer when the page loads, and my code is like following:
function App() {
const [sec, setSec] = useState(0);
useEffect(()=>{
setInterval(()=>{setSec(prev=>prev+1)
}, 1000)}, [sec])
return (
<span>{sec}</span>
)}
But the counter increments not just every second but very fast. I didnt know what the issue is.
I think the setInterval()
will run every second, which updates the sec
variable, which in turn would calls the callback function in useEffect()
so it can run again.
I did find some example code for setting a react timer online but they use useRef()
to hold a setInterval()
callback function and then update the sec
variable like setSec(sec+1)
. I loosely understand the logic but not completely, and I can't figure out why my code does not work.
Thank you so much if anyone can help me. Sorry if it is some simple thing that I missed.
I see two problems.
First, remove sec
from the dependency array on useEffect
. You should use an interval or an effect dependency, but not both. When you use both you're essentially creating a new interval on every re-render, so every re-render will increase the amount of increments and the rate will compound indefinitely.
If you want to rely on the dependency array and the effect re-running on every render, you'd just use setTimeout
instead of setInterval
. For example:
useEffect(() => {
setTimeout(() => {
setSec((prev) => prev + 1);
}, 1000);
}, [sec]);
That way each re-render of the component (and each re-invoking of the effect) only performs one increment, not a new ongoing interval of incremenets.
Second, and more subtly, the effect is leaving a side-effect that isn't cleaned up. This will be evident in a development environment if the component technically renders twice, and you'd see your counter increment by 2 instead of 1 every second.
You can "clean up" an effect by returning a cleanup function from the useEffect
callback. React would use that function when the component unloads. For example:
useEffect(() => {
const x = setInterval(() => {
setSec((prev) => prev + 1);
}, 1000);
// return a cleanup function here
return () => clearInterval(x);
}, []);