I got a strange behaviour in my react code:
In development (strict) mode, the following component did not clear interval successfully in clear function when interval is set to 1000 milliseconds. If I change the interval to slightly larger value (e.g. 1010 milliseconds). The callback can be stopped when component unmount. I have no ideas why the code behave like this. Anyone knows why it have such strange behaviour and how should I fix it?
function Timer() {
const [secondsPassed, setSecondPassed] = useState(0);
const [rows, setRow] = useState(1);
const thresold = 60;
useEffect(() => {
console.log("effect start");
function onInterval() {
setSecondPassed(prevSec => {
let currentSecond = prevSec + 1;
console.log(currentSecond);
if (currentSecond % thresold === 0) {
setRow(prevRow => prevRow + 1);
}
return currentSecond;
});
}
const intervalId = setInterval(onInterval, 1000);
console.log(`setinterval: ${intervalId}`);
return () => {
console.log(`clearInterval: ${intervalId}`);
clearInterval(intervalId);
}
}, []);
return (
<>
<div className="box-x" style={{ width: secondsPassed % thresold }}></div>
</>
);
}
However, this clock component works in the same app just fine.
function Clock() {
const [now, setNow] = useState(new Date());
useEffect(() => {
const intervalId = setInterval(() => {
setNow(new Date());
}, 1000);
return () => {
clearInterval(intervalId);
}
}, []);
return (<h2 >Time now is {now.getSeconds()}</h2>);
}
If I change to use setTimeout()
and setTimeout()
instead, it works fine with 1000 milliseconds as well.
Environment:
- Node:
v22.3.0
- React:
18.3.1
- vscode:
1.93.1
- Chrome:
127.0.6533.120 arm64
2
Answers
Please see the dependency array, it now is empty. It means the code inside the hook will run only on two events – mount and unmount of the components. The code inside the hook will run on mount, and the code returned by this code will run on unmount. That is the reason the clearInterval now works only once. However this code works fine and gives you the correct results.
If you still want to see how to force a clearInterval, please see below one of the ways for doing it.
Steps to do:
a) Please add a dependency to the hook.
b) The onInterval function has to move out of the hook.
And then set it as its dependency.
c) What would now happen is that on each render, the function onInterval
will be re-defined and will result a new functional object.
d) This will cause the dependency in the hook to be identified as a
change.
e) And that will lead to a re-synchronise action.
f) Every re-synchronise action will start with the execution of the
returned function and then proceed with the handler of the hook.
App.js
Test run
As it is in the another answer, the useEffect hook in the following code also works only on two events because of the empty dependency array. You may please check and see, the statement console.log(‘clearInterval’) executes only on unmount – you can mock that event by page refresh)