Issue
I've tried different ways, but It doesn't works.
[...]
const [automatic, setAutomatic] = useState(false);
[...]
var startAuto;
useEffect(() => {
if (!automatic) {
console.log("stop");
clearInterval(startAuto);
} else {
startAuto = setInterval(() => {
changeQuestion("+");
}, 5 * 1000);
}
}, [automatic]);
[...]
<Button
onPress={() => setAutomatic(!automatic)}
title="turn on/off"
/>
[...]
It works when I put a setTimeout outside the useEffect, that way:
setTimeout(() => { clearInterval(startAuto); alert('stop'); }, 10000);
But I want to have a button to start / stop
Solution
Your var startAuto;
is redeclared on each render, and since changing the state causes a re-render, it never holds the reference to the interval, which is never cleared.
Use the useEffect
cleanup function to clear the interval. Whenever automatic
changes, it would call the cleanup
(if returned by the previous invocation), and if automatic
is true
it would create a new interval loop, and return a new cleanup function of the current interval.
useEffect(() => {
if(!automatic) return;
const startAuto = setInterval(() => {
changeQuestion("+");
}, 5 * 1000);
return () => {
clearInterval(startAuto);
};
}, [automatic]);
Working example:
const { useState, useEffect } = React;
const Demo = () => {
const [automatic, setAutomatic] = useState(false);
const [question, changeQuestion] = useState(0);
useEffect(() => {
if(!automatic) return;
const startAuto = setInterval(() => {
changeQuestion(q => q + 1);
}, 5 * 100);
return () => {
clearInterval(startAuto);
};
}, [automatic]);
return (
<div>
<button
onClick={() => setAutomatic(!automatic)}
>
turn {automatic ? 'off' : 'on'}
</button>
<p>{question}</p>
</div>
);
}
ReactDOM
.createRoot(root)
.render(<Demo />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>
Answered By - Ori Drori
Answer Checked By - - Cary Denson (ReactFix Admin)