In a simple timer component, I want to start and stop it with buttons, but the interval does not stop with a simple clearInterval function. Is there something I am missing?
import React, { useState } from 'react'
export default function Timer3() {
const [seconds, setseconds] = useState(0)
let intervalId;
const startTimer = () => {
intervalId = setInterval(() => {
setseconds((pre) => pre + 1)
}, 1000)
}
const stopTimer = () => {
clearInterval(intervalId)
}
return (
<>
{seconds}
<button onClick={startTimer}>start</button>
<button onClick={stopTimer}>stop</button>
</>
)
}
I know that in the code above, there is a bug: if I press "Start" twice, it will count twice. I am going to fix this after finding out how to make it stop. There is also a solution on the web to use useEffect to run the timer, but I am not looking for code—just a simple answer as to why it does not work.
The issue is that each time the state updates it re-renders the component and the intervalId
variable is redeclared and any previous value is lost, so when the stop button is clicked it doesn't have a reference to the running timer's id.
You can fix this by using a React ref to store the interval id value. React refs persist render cycle to render cycle.
function Timer3() {
const [seconds, setseconds] = React.useState(0);
const intervalId = React.useRef();
React.useEffect(() => {
// Clear any running timers when component unmounts
return () => {
clearInterval(intervalId.current);
}
}, []);
const startTimer = () => {
// Clear any running timers when re-starting
clearInterval(intervalId.current);
intervalId.current = setInterval(() => {
setseconds((pre) => pre + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalId.current);
};
return (
<>
{seconds}
<button onClick={startTimer}>start</button>
<button onClick={stopTimer}>stop</button>
</>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<Timer3 />
</React.StrictMode>
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>
<div id="root" />