Когда использовать Ref, а когда — State

Выбор между useRef и useState определяется одной простой идеей: нужно ли, чтобы изменение значения вызывало перерисовку компонента?

Используйте useState, если…

Значение влияет на то, что пользователь видит. Например:

  • Текст в поле ввода, который отображается где-то ещё
  • Состояние кнопки (активна/неактивна)
  • Выбранная вкладка или тема интерфейса
  • Список задач, фильтры, ошибки валидации
const [count, setCount] = useState(0);
// При setCount(1) компонент перерисуется — это необходимо,
// если, например, вы показываете "Счётчик: 1"

useState — основной инструмент для управления данными, которые определяют внешний вид приложения.

Используйте useRef, если…

Значение нужно только для логики, но не влияет на отображение. Например:

  • Ссылка на DOM-элемент (для фокуса, прокрутки, измерения)
  • Хранение предыдущего значения пропса или состояния
  • Идентификатор таймера (setTimeout, setInterval)
  • Кэш дорогих вычислений
  • Счётчик попыток, логирование, технические данные
const timerIdRef = useRef(null);
// Изменение timerIdRef.current не должно перерисовывать UI —
// оно нужно только для остановки таймера позже

Практическое правило

Если вы используете значение внутри JSX ({value}), — это состояние.
Если вы используете значение только в функциях, эффектах или обработчиках, — это ref.

Пример выбора в формах

  • Контролируемая форма: useState — потому что вы можете показывать ошибку, кнопку активировать/деактивировать, делать предпросмотр.
  • Неконтролируемая форма: useRef — если вы просто читаете данные при отправке и ничего не отображаете на основе ввода.

Ошибки при выборе

  • Использовать ref для данных в JSX — компонент не обновится, даже если значение изменилось.
  • Использовать state для таймеров или DOM-ссылок — это вызовет лишние ререндеры и усложнит код.

В подавляющем большинстве случаев начинайте с useState. Переходите к useRef только тогда, когда убедитесь, что перерисовка не нужна и мешает производительности или логике.