Значения, которые не отрисовываются
Реф может хранить любое значение, не только элемент. Представьте его как коробку, которую React хранит для компонента от одной отрисовки к другой: вы кладёте что-то в ref.current, и в следующей отрисовке оно всё ещё там. А в отличие от состояния, изменение ref.current не заставляет React отрисовывать компонент заново.
Это как раз то, что нужно для вещей, которые нужны вашему коду, но не показываются на странице. Типичный пример — id интервала, который один клик запускает, а другой останавливает:
const intervalRef = useRef(null);
function handleStart() {
intervalRef.current = setInterval(() => {
setSeconds((seconds) => seconds + 1);
}, 1000);
}
function handleStop() {
clearInterval(intervalRef.current);
}Почему не что-то другое?
- Обычная переменная,
let intervalId;в компоненте, не переживает отрисовку. При каждой отрисовке React снова вызывает функцию компонента, и она начинает с новой, пустойintervalId. Через секунду интервал изменил время, компонент отрисовался заново, а id пропал: кнопке «Стоп» нечего останавливать. - Состояние сохранило бы id, но каждое его изменение вызывает новую отрисовку, а страница id вообще не показывает.
Итак: состояние — для того, что показывает страница, реф — для того, что нужно только вашему коду.
В секундомере эффект запускал интервал, как только секундомер начинал идти, а очистка его останавливала. Здесь интервал запускают и останавливают сами обработчики кликов, поэтому его id должен где-то ждать между двумя кликами.
Задание
- 1
Создайте
intervalRefчерезuseRef(null). ВhandleStartзапустите интервал, который каждые 1000 миллисекунд прибавляет 1 кseconds, и сохраните его id вintervalRef.current. - 2
В
handleStopостановите интервал:clearInterval(intervalRef.current). - 3
Нажмите «Старт» дважды: запустятся два интервала, время начнёт расти на 2 каждую секунду, а «Стоп» остановит только второй. Сделайте так, чтобы повторный «Старт» ничего не делал: в начале
handleStartзавершайте функцию черезreturn, еслиintervalRef.currentне равенnull. А вhandleStopвозвращайтеintervalRef.currentвnull, чтобы «Старт» снова работал после «Стоп».