> В чем разница 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. Это приводит к багам, когда интерфейс не синхронизирован с данными.
Пример кода
JSXimport { 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 не обнаружит изменение
> Похожие задачи по frontend
Какие проблемы могут возникнуть при использовании контекста в React
Изменится ли поведение методов при передаче их как пропсов в другой React-компонент
Что делают хуки useSelector и useDispatch в Redux
Почему форма отправляется два раза при сабмите в React
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью