Values That Don't Render
A ref can hold any value, not just an element. Think of it as a box that React keeps for the component from one render to the next: you put something into ref.current, and it's still there in the next render. And unlike state, changing ref.current doesn't make React render again.
That's just right for things that your code needs but the page doesn't show. A typical one is the id of an interval that one click starts and another click stops:
const intervalRef = useRef(null);
function handleStart() {
intervalRef.current = setInterval(() => {
setSeconds((seconds) => seconds + 1);
}, 1000);
}
function handleStop() {
clearInterval(intervalRef.current);
}Why not something else?
- A plain variable,
let intervalId;in the component, doesn't survive a render. At each render, React calls the component function again, and it starts with a new, emptyintervalId. A second later, the interval has changed the time, the component has rendered again, and the id is gone: Stop has nothing to stop. - State would keep the id, but every change of state renders again, and the page doesn't show the id at all.
So: state for what the page shows, a ref for what only your code needs.
In the stopwatch, an effect started the interval whenever the stopwatch began to run, and the cleanup stopped it. Here, the click handlers start and stop the interval themselves, so its id has to wait somewhere between the two clicks.
Instructions
Create intervalRef with useRef(null). In handleStart, start an interval that adds 1 to seconds every 1000 milliseconds, and keep its id in intervalRef.current.
In handleStop, stop the interval with clearInterval(intervalRef.current).
Click Start twice: two intervals run, the time goes up by 2 every second, and Stop stops only the second one. Make a second Start do nothing: at the start of handleStart, end the function with return when intervalRef.current isn't null. And in handleStop, set intervalRef.current back to null, so that Start works again after Stop.