Как работать с DOM-элементами в React, используя рефы?
Что проверяют
Знаете ли, чем useRef отличается от состояния, и понимаете ли, что реф это запасной выход для работы с DOM, а не способ управлять интерфейсом в обход React.
Как отвечать
Что такое реф. useRef(initial) возвращает стабильный объект с единственным полем current, который переживает все рендеры компонента. Запись в current не вызывает перерисовку. В классовых компонентах ту же роль играет React.createRef().
Привязка к DOM. Реф передаётся через проп ref, React записывает туда узел после коммита и null при размонтировании. В React 19 ref это обычный проп функционального компонента, forwardRef больше не нужен. В более старых версиях без него реф до внутреннего узла не доходил, и это до сих пор частый вопрос на собеседовании.
function SearchBox() {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>Найти</button>
</>
);
}Когда реф оправдан: управление фокусом, выделение текста, программный скролл, воспроизведение медиа, замер размеров и позиции, императивные анимации, интеграция с библиотеками, которые сами рисуют в DOM (карты, графики, текстовые редакторы).
Второе применение, изменяемое значение между рендерами: id таймера, предыдущее значение пропса, флаг «форма уже отправлена», счётчик, который не отображается. Ключевой критерий выбора: если значение влияет на отрисовку, ему место в состоянии; если нет, в рефе.
Ref-колбэк вместо объекта берут, когда узел надо обработать в момент привязки. В React 19 такая функция может вернуть функцию очистки:
<div ref={(node) => {
const observer = new ResizeObserver(handleResize);
if (node) observer.observe(node);
return () => observer.disconnect();
}} />Ограничения. Читать и писать ref.current во время рендера нельзя, только в эффектах и обработчиках: на момент рендера узла ещё не существует, а мутация в фазе рендера ломает поведение конкурентного React. Чтобы отдать наружу не весь DOM-узел, а ограниченный набор методов вроде focus и reset, используют useImperativeHandle.
Где спотыкаются
Берут реф вместо состояния и правят DOM напрямую (ref.current.textContent = "..."), после чего следующий рендер молча затирает изменения. Вторая частая ошибка, обращение к ref.current в теле компонента, где он ещё null, вместо useEffect.