Как useRef взаимодействует с DOM?
Хуки
useRef взаимодействует с DOM через атрибут ref: React сам кладёт ссылку на созданный DOM-узел в ref.current после коммита — дальше с узлом можно работать напрямую.
Механика подключения
const SearchInput = () => {
const inputRef = useRef(null); // 1. контейнер { current: null }
useEffect(() => {
inputRef.current.focus(); // 3. узел доступен после коммита
}, []);
return <input ref={inputRef} />; // 2. React привяжет узел
};Жизненный цикл привязки:
- при монтировании React создаёт DOM-элемент и записывает его в
inputRef.current(до этого — null, поэтому в теле рендера узла ещё нет); - при размонтировании — записывает null;
- при обновлениях ссылка сохраняется (узел переиспользуется reconciliation'ом).
Что делать с узлом
Императивные операции, не выражаемые декларативно: focus()/blur(), scrollIntoView(), getBoundingClientRect() (измерения), select(), play/pause у медиа, привязка сторонних библиотек (чарты, карты — им отдают контейнер).
Правила
- Работать с
ref.currentтолько в эффектах и обработчиках — не в теле рендера (узла может не быть, рендер должен быть чистым). - Не менять через ref то, чем управляет React (value управляемого инпута, классы из JSX) — перезапишется при следующем рендере.
- Ref на кастомный компонент — через forwardRef (до React 19) или проп ref (React 19); наружу лучше отдавать узкий API через useImperativeHandle.
- Альтернатива для реакции на появление узла — callback ref:
<div ref={(node) => ...} />вызывается при присоединении/отсоединении.
Коротко для собеседования
useRef создаёт контейнер, а атрибут ref привязывает его к DOM: React заполняет current после коммита (null до монтирования и после размонтирования). Использовать в эффектах/обработчиках для фокуса, измерений, скролла, интеграций; не трогать управляемые React свойства; для отслеживания появления узла — callback ref.