> В чем разница useRef и useState в React (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Aston, IT-One

Стек: React

> Пример ответа

Короткий ответ

useRef и useState - оба хуки React для хранения данных, но с разными целями. useState вызывает ререндер компонента при изменении значения, а useRef - нет. useRef возвращает мутируемый объект с полем .current, который сохраняется между рендерами. useState используется для данных, влияющих на UI, useRef - для ссылок на DOM-элементы, таймеров, предыдущих значений или любых данных, которые не должны вызывать перерисовку.

Подробное объяснение

Основное различие лежит в управлении жизненным циклом и реактивностью. useState - это реактивное состояние: при каждом вызове setState React помечает компонент как "грязный" и планирует ререндер. Это необходимо, когда изменение данных должно отразиться на интерфейсе. Значение useState иммутабельно - вы заменяете его целиком через сеттер.

useRef, напротив, не реактивен. Он создаёт обычный JavaScript-объект { current: initialValue }, который существует на протяжении всего времени жизни компонента. Мутация .current не вызывает ререндер, потому что React не отслеживает изменения ref. Это делает useRef идеальным для:

  • хранения ссылок на DOM-узлы (через атрибут ref)
  • сохранения идентификаторов таймеров (setInterval/setTimeout)
  • хранения предыдущих значений (паттерн с useEffect)
  • любых данных, которые нужно "запомнить" между рендерами, но не показывать в UI

Важный нюанс: изменение .current в useRef не прерывает текущий рендер и не запускает новый. useState же может быть асинхронным (в React 18 с автоматическим батчингом это менее заметно, но всё ещё актуально для старых версий).

На практике

Выбор между хуками диктуется вопросом: "должно ли изменение этого значения вызывать перерисовку интерфейса?".

Используйте useState когда:

  • данные отображаются в JSX
  • изменение данных должно немедленно обновить UI
  • вы работаете с формами, переключателями, списками

Используйте useRef когда:

  • нужно получить доступ к DOM-элементу (фокус, измерение размеров)
  • хранить callback или таймер, который не должен "устаревать" при ререндерах
  • нужно сохранить предыдущее значение для сравнения
  • данные используются внутри эффектов или обработчиков, но не влияют на разметку

Популярный антипаттерн - пытаться использовать useRef для хранения состояния, которое должно обновлять UI. Это приводит к багам, когда интерфейс не синхронизирован с данными.

Пример кода

JSX
import { useState, useRef, useEffect } from 'react';
function Timer() {
// useState для отображаемого значения
const [seconds, setSeconds] = useState(0);
// useRef для хранения ID таймера (не влияет на UI)
const intervalRef = useRef(null);
// useRef для хранения предыдущего значения
const prevSecondsRef = useRef(0);
useEffect(() => {
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
// Сохраняем предыдущее значение после каждого рендера
useEffect(() => {
prevSecondsRef.current = seconds;
});
const prevSeconds = prevSecondsRef.current;
return (
<div>
<p>Текущее: {seconds}, Предыдущее: {prevSeconds}</p>
<button onClick={() => clearInterval(intervalRef.current)}>
Стоп (через ref)
</button>
</div>
);
}

Как отвечать на собеседовании

Начните с ключевого отличия: реактивность. Скажите, что useState вызывает ререндер, useRef - нет. Приведите конкретные сценарии использования: DOM-ссылки, таймеры, предыдущие значения для useRef; формы, счётчики, видимые данные для useState.

Упомяните, что useRef возвращает мутируемый объект, а useState - иммутабельное значение с сеттером. Добавьте про асинхронность setState в старых версиях React и батчинг в 18-й.

Если спросят про производительность - объясните, что useRef не вызывает лишних рендеров, но его мутации не видны в UI без дополнительного триггера (например, через useState или forceUpdate).

Что проверяет интервьюер

  • Понимание жизненного цикла компонента и механизма ререндера
  • Умение различать реактивные и нереактивные данные
  • Знание практических паттернов (DOM refs, stale closures, previous value)
  • Понимание иммутабельности в React
  • Способность объяснить trade-off между хуками

Типичные ошибки

  • Использование useRef для хранения состояния, которое должно обновлять UI - интерфейс не будет перерисовываться
  • Попытка читать useRef.current в рендере и ожидать, что изменение вызовет ререндер - этого не произойдёт
  • Забывание, что изменение useState асинхронно в старых версиях React - чтение состояния сразу после setState даст старое значение
  • Использование useState для DOM-ссылок вместо useRef - избыточные ререндеры и потеря производительности
  • Мутация объекта в useState напрямую (state.user.name = 'new') вместо создания нового объекта - React не обнаружит изменение

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью