I am trying to implement a countdown, but the state is not being update as expected. It stays stuck on initial value 30. I have no clue how to solve it. Can anyone help me please?
const [timer, setTimer] = useState(30);
function handleTimer() {
const interval = setInterval(() => {
setTimer((count) => count - 1);
if (timer <= 0) {
clearInterval(interval);
}
}, 1000);
}
useEffect(() => {
handleTimer();
}, []);
CodePudding user response:
The problem is about javascript closures, you can read more about it here
Also, Dan has a full detailed article talking about this specific problem. I strongly suggest you read it.
And here is a quick solution and demonstration for your problem. First of all, the useEffect
will be executed every time the component is remount. And this could happen in many different scenarios depending on your code. Hence, The useEffect
starts fresh and closes on new data every time.
So all we need is to save our values into ref so we can make use of the same reference every re-render.
// Global Varibales
const INITIAL_TIMER = 30;
const TARGET_TIMER = 0;
// Code refactoring
const [timer, setTimer] = useState(INITIAL_TIMER);
const interval = useRef();
useEffect(() => {
function handleTimer() {
interval.current = setInterval(() => {
setTimer((count) => count - 1);
}, 1000);
}
if (timer <= TARGET_TIMER && interval.current) {
clearInterval(interval.current);
}
if (timer === INITIAL_TIMER) {
handleTimer();
}
}, [timer]);