Использование useRef для хранения значений

Хук useRef можно использовать не только для доступа к DOM-элементам, но и для хранения любых изменяемых значений, которые должны сохраняться между рендерами, но не должны вызывать перерисовку компонента.

Как это работает

Значение, хранящееся в ref.current, остаётся неизменным в течение всего жизненного цикла компонента, если вы его явно не измените. При этом:

  • Изменение ref.current не запускает повторный рендер.
  • Значение доступно во всех обработчиках, эффектах и даже внутри замыканий без потери актуальности (в отличие от переменных в теле компонента).

Типичные сценарии

Хранение предыдущего значения

Полезно для сравнения текущего и предыдущего состояния или пропса:

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

function Counter({ count }) {
  const prevCount = usePrevious(count);
  return <p>Сейчас: {count}, было: {prevCount}</p>;
}

Управление таймерами

Чтобы избежать создания новых таймеров при каждом рендере:

function Timer() {
  const intervalRef = useRef(null);

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      console.log('Тик');
    }, 1000);

    return () => clearInterval(intervalRef.current);
  }, []);

  function stopTimer() {
    clearInterval(intervalRef.current);
  }

  return <button onClick={stopTimer}>Остановить</button>;
}

Кэширование дорогих вычислений

Если вычисление зависит от внешних данных и не должно зависеть от ререндера:

function ExpensiveComponent({ data }) {
  const cacheRef = useRef(null);

  if (!cacheRef.current || cacheRef.current.input !== data) {
    cacheRef.current = {
      input: data,
      output: heavyComputation(data)
    };
  }

  return <div>{cacheRef.current.output}</div>;
}

Отличие от useState

  • useState — для данных, влияющих на UI (изменение → рендер).
  • useRef — для данных, нужных «внутри» логики, но не отображаемых напрямую.

Например, счётчик попыток авторизации, который не отображается, но используется в фоне — хороший кандидат для ref.

Важные особенности

  • Не мутируйте ref.current во время рендера (например, внутри основного тела компонента). Это может привести к непредсказуемому поведению.
  • Значение в ref не сериализуется и не сохраняется при горячей перезагрузке (HMR), если не предусмотрена дополнительная логика.

Использование useRef для хранения значений — это способ сохранить "память" компонента без лишних ререндеров.