Timers and Cleanup
Effects are also the place for timers. In the JavaScript course, you started them with setInterval() and stopped them with clearInterval(). In React, an effect starts them. Here's a countdown to the opening of the festival's gates:
import { useEffect, useState } from "react";
export default function GatesCountdown() {
const [secondsLeft, setSecondsLeft] = useState(60);
useEffect(() => {
const id = setInterval(() => {
setSecondsLeft((secondsLeft) => secondsLeft - 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <p>The gates open in {secondsLeft} s</p>;
}Three things matter here:
- The dependencies
[]. The interval starts once, when the component appears, and then runs on its own. Without the array, the effect would run again after every render, so every second, and stop and start the interval each time. - The updater function. The interval's function was made in the first render, when
secondsLeftwas 60, and it keeps that value.setSecondsLeft(secondsLeft - 1)would set 59 again and again. The updater function gets the current value each time. - The cleanup. The function that the effect returns is its cleanup. React calls it when the component disappears from the page, and also before it runs the effect again because a dependency has changed. Here, it stops the interval.
Why does an interval need a cleanup? A component can disappear while its interval is still running. In this exercise, the timer disappears when the set ends. Without the cleanup, the interval doesn't know that: it keeps changing the state of a component that's no longer on the page, for as long as the page is open, and each time the timer is shown again, one more interval joins it.
Instructions
In StageTimer.jsx, add an effect with the dependencies [] that starts an interval: every 1000 milliseconds, it increases seconds by 1 with the updater function (seconds) => seconds + 1. Keep the interval's id in a constant id: you'll need it in the next step.
Stop the interval with a cleanup: the effect returns () => clearInterval(id). Before you add it, see what it changes: put console.log("tick"); into the interval's function, end the set and look at the console. The ticks go on. With the cleanup, they stop when the set ends.