> Как работать с DOM-элементами в React, используя рефы? (React)

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

Компании: Точка, Сбер

Стек: React

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

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

В React для работы с DOM-элементами используются рефы (refs), создаваемые через useRef (в функциональных компонентах) или createRef (в классовых). Рефы позволяют напрямую обращаться к DOM-узлам, минуя декларативный подход React. Основные сценарии: фокус на элементе, работа с медиа, интеграция с библиотеками, измерение размеров. Рефы не следует использовать для управления состоянием или частых мутаций DOM - для этого есть state и эффекты.

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

Рефы в React - это механизм доступа к DOM-элементам или React-компонентам напрямую. В функциональных компонентах используется хук useRef, который возвращает mutable-объект с свойством .current. При передаче рефа в JSX через атрибут ref, React после монтирования устанавливает .current в соответствующий DOM-узел.

Важно понимать, что рефы - это escape hatch для случаев, когда декларативный подход неэффективен или невозможен. Типичные use cases:

  • управление фокусом, выделением текста
  • интеграция с DOM-библиотеками (D3, jQuery)
  • работа с медиа-элементами (video, audio)
  • анимации, требующие прямого доступа к DOM
  • измерение размеров или позиции элементов

Рефы не вызывают ререндер при изменении .current, в отличие от state. Это делает их подходящими для хранения значений, не влияющих на отображение. Однако злоупотребление рефами нарушает реактивную парадигму и усложняет поддержку кода.

На практике

При работе с рефами в React важно соблюдать несколько правил:

  • Используй useRef для функциональных компонентов, createRef - для классовых
  • Не читай .current до монтирования компонента - используй useEffect или callback refs
  • Для динамического количества элементов используй callback refs или маппинг рефов
  • При передаче рефа дочернему компоненту используй forwardRef
  • Для доступа к предыдущему значению используй useRef в комбинации с useEffect

Callback refs дают больше контроля: они вызываются при каждом изменении рефа, позволяя выполнять cleanup. Это полезно для анимаций или интеграции с внешними библиотеками.

Пример кода

JSX
import { useRef, useEffect, forwardRef } from 'react';
// Базовый пример: фокус на инпуте
function InputWithFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
return <input ref={inputRef} />;
}
// Пример с forwardRef для дочернего компонента
const FancyInput = forwardRef((props, ref) => {
return <input ref={ref} className="fancy" {...props} />;
});
function Parent() {
const fancyRef = useRef(null);
const handleClick = () => {
fancyRef.current?.focus();
};
return (
<>
<FancyInput ref={fancyRef} />
<button onClick={handleClick}>Focus</button>
</>
);
}
// Callback ref для измерения размеров
function MeasurableComponent() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<>
<div ref={measuredRef}>Content</div>
<p>Height: {height}px</p>
</>
);
}

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

Начни с определения рефов и их назначения. Подчеркни, что это механизм для прямого доступа к DOM, но использовать его стоит только когда декларативный подход не подходит. Приведи 2-3 типичных сценария использования. Упомяни разницу между useRef и createRef, а также forwardRef для передачи рефа через компоненты. Обязательно скажи, что рефы не вызывают ререндер и не должны использоваться для управления состоянием. Если спросят про производительность - отметь, что callback refs могут быть эффективнее при динамических списках.

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

Интервьюер оценивает понимание:

  • разницы между декларативным и императивным подходами в React
  • когда оправдано использование рефов, а когда - нет
  • механизма работы useRef и его отличий от state
  • передачи рефов через forwardRef
  • правильного использования callback refs и cleanup
  • потенциальных проблем (чтение до монтирования, stale closures)

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

  • Использование рефов для хранения состояния, которое влияет на рендер - это приводит к багам, так как изменения не вызывают ререндер
  • Чтение .current в рендер-фазе до монтирования компонента - получаем null
  • Создание рефа внутри рендера без useRef - каждый рендер создаёт новый объект
  • Использование рефов для частых DOM-мутаций вместо state - теряются преимущества React
  • Забывание про forwardRef при передаче рефа в кастомный компонент - получим warning в консоли
  • Неправильный cleanup в callback refs - утечки памяти при интеграции с внешними библиотеками

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

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