Refs в React — доступ к DOM-узлам

Refs в React дают безопасный способ получить ссылку на созданный компонентом DOM-элемент или на экземпляр компонента. Используйте refs, когда нужно вызвать браузерные API (например, focus, select, измерение размеров) или интегрировать сторонние библиотеки. Предпочитайте декларативные решения; применяйте refs целенаправленно и с ограниченной зоной ответственности.
Быстрые ссылки
- Зачем нужны refs
- Создание ref
- Назначение ref компонентам React
- Refs и функциональные компоненты
- Использование ref внутри функциональных компонентов
- Callback refs
- Когда refs не подходят
- Альтернативы
- План действий и чек-листы
- Заключение
Зачем нужны refs
Refs — это механизм, который позволяет напрямую получить доступ к DOM-узлам или к экземплярам классовых компонентов. Он нужен, когда вам нужно выполнить действие, которое нельзя выразить декларативно через props или state. Частые сценарии:
- поставить фокус в поле ввода после валидации;
- запускать/пауза HTMLMediaElement (audio/video);
- управлять анимациями или измерять размеры и положение элементов;
- интегрировать сторонние библиотеки, которые ожидают DOM-элемент.
Без refs вы обычно оперируете JSX и позволяете React управлять DOM за вас. React под капотом вызывает браузерные API вроде
document.createElement()и
document.appendChild()чтобы создать структуру DOM. Refs дают исключение из декларативного подхода, позволяя явно обратиться к созданному узлу.
Создание ref
Обычно ref создают через
React.createRef()и сохраняют в свойстве экземпляра компонента. Затем ref передается через специальный prop ref — это не обычный проп, и внутренняя логика компонента к нему не обращается.
class DemoComponent extends React.Component {
inputRef = React.createRef();
focusInput = () => this.inputRef?.current.focus();
render() {
return (
);
}
}Объект ref имеет свойство current, которое либо содержит DOM-узел, либо null, если элемент ещё не отрендерен. По этой причине часто используют безопасную проверку (например, Optional Chaining ?.).
Назначение refs компонентам React
Ref можно назначить не только нативным HTML-элементам, но и экземплярам классовых компонентов. В этом случае ref.current будет ссылкой на экземпляр компонента, и вы сможете вызвать его методы.
class View extends React.Component {
state = { error: true };
formRef = React.createRef();
focusForm = () => this.formRef?.current.focusInput();
submit = () => {
if (this.state.error) {
alert("There was an error; check your input.");
this.focusForm();
}
};
render() {
return (
);
}
}
class Form extends React.Component {
inputRef = React.createRef();
focusInput() {
this.inputRef.current?.focus();
}
render() {
return
}
}Важно: не используйте refs как замену передачи данных через props. Refs уместны, когда взаимодействие с DOM или экземпляром действительно необходимо.
Refs и функциональные компоненты
Функциональные компоненты не имеют экземпляров, поэтому напрямую им нельзя передать ref. Для этой цели существует механизм “forwarding” — пересылка ref далее к одному из дочерних элементов. Его реализуют через React.forwardRef:
const InputComponent = React.forwardRef((props, ref) => (
));Функция, переданная в forwardRef, получает два аргумента: props и сам forwarded ref.
Использование ref внутри функциональных компонентов
В функциональных компонентах вы можете создать локальный ref через хук useRef():
const InputComponent = props => {
const ref = useRef();
return
};useRef() возвращает объект с полем current, который сохраняется между рендерами. Хук хорош не только для доступа к DOM — он также используется для хранения любого состояния, которое не должно вызывать повторный рендер при изменении (например, таймеры, предыдущие значения, mutable-объекты).
Callback refs
Альтернативный способ — callback refs. Вместо создания объекта через createRef() вы присваиваете ref функцию. React вызовет её при монтировании и размонтировании, передавая DOM-узел или null.
class CallbackRef extends React.Component {
render() {
return this.inputRef = el} />
}
}Callback refs дают больший контроль (вы можете выполнять дополнительные действия при получении узла), но их стоит применять осознанно, чтобы не усложнять код.
Когда refs не подходят
Важно понимать, что refs ломают изоляцию и top-down поток данных. Вот ситуации, когда refs — плохой выбор:
- Вы пытаетесь заменить передачу данных через props.
- Нужна синхронизация состояния между компонентами — используйте поднятие состояния или менеджер состояний.
- Вы хотите отследить пользовательский ввод — лучше валидировать через контролируемые компоненты (value + onChange).
Пример провала: вы используете refs для передачи данных от дочернего компонента вверх вместо события/колбека. Это ведёт к хрупкой архитектуре.
Альтернативы
Перед использованием refs рассмотрите альтернативы:
- Управляемые (контролируемые) компоненты: value + onChange.
- Колбэки и события в props для общения между компонентами.
- Контекст React для глобальных зависимостей.
- Плагины/мидлвары, адаптирующие сторонние библиотеки к декларативной модели.
Выбирайте декларативный путь всегда, когда он покрывает задачу.
Модель принятия решения
Используйте простую эвристику:
- Можно ли решить задачу через props/state/context? Если да — так и сделайте.
- Нужно ли взаимодействовать с браузерным API (focus, измерение, media control)? Если да — ref оправдан.
- Требуется ли доступ к экземпляру компонента? Только для классовых компонентов используйте ref.
- Требуется ли пересылка ref вниз? Используйте forwardRef.
Ниже — Mermaid-диаграмма для быстрой навигации.
flowchart TD
A[Нужно управлять элементом?] --> B{Декларативно?}
B -- Да --> C[Использовать props/state/context]
B -- Нет --> D{Нужен прямой доступ к DOM?}
D -- Да --> E[Использовать ref или forwardRef]
D -- Нет --> F[Пересмотреть архитектуру]Практические примеры и советы
- Фокус после валидации:
this.inputRef?.current?.focus();- Измерение размеров для анимации:
const rect = this.elementRef.current.getBoundingClientRect();- Интеграция jQuery-плагина:
componentDidMount() {
$(this.containerRef.current).pluginInit();
}Совет: при интеграции сторонних библиотек используйте отдельный контейнерный компонент, который инкапсулирует работу с DOM и exposes минимальный контракт через props.
Факты и полезные числа
React.createRef()возвращает новый объект при каждом вызове, поэтому обычно его создают один раз на экземпляр компонента.useRef()возвращает один и тот же объект на протяжении жизни компонента.ref.currentможет бытьnullдо первой отрисовки и после размонтирования.
Эти факты помогут избежать гонок и ошибок времени жизни компонентов.
Чек-листы для ролей
Разработчик интерфейса
- Проверить, можно ли решить задачу декларативно.
- Если нужен ref, ограничить область его использования.
- Добавить проверки на
nullперед использованиемcurrent. - Покрыть кейсы монтирования и размонтирования.
Тимлид
- Оценить влияние refs на архитектуру.
- Убедиться, что refs не используются для передачи данных между компонентами.
- Попросить покрыть критические сценарии тестами.
QA инженер
- Тестировать сценарии фокуса и взаимодействия с клавиатурой.
- Проверить корректность работы при быстром монтировании/размонтировании.
План действий и SOP
Шаги при добавлении ref в кодовую базу:
- Оцените: можно ли обойтись без ref? Документируйте аргументы.
- Выберите тип ref:
createRef,useRef, callback илиforwardRef. - Инкапсулируйте обращения к DOM в одном месте (компонент-обёртка).
- Добавьте безопасные проверки на
null. - Покройте изменения тестами: unit-тесты для логики, E2E для UX (фокус/взаимодействие).
- Обновите документацию компонента с указанием публичного API.
Критерии приёмки
- Все сценарии использования refs неразрывно описаны в PR.
- Нет случаев передачи данных через ref вместо props или событий.
- Код обработан на случай
nullвref.current. - Тесты покрывают фокус, кастомные методы и интеграцию с внешней библиотекой.
Матрица рисков и смягчения
- Риск: связность компонентов выросла. Смягчение: документировать публичные методы и гарантировать малую поверхность взаимодействия.
- Риск: гонки при монтировании/размонтировании. Смягчение: проверки на
null, очистка в componentWillUnmount / useEffect cleanup. - Риск: ухудшение тестируемости. Смягчение: инкапсуляция DOM-взаимодействия и мокирование в тестах.
Безопасность и приватность
Refs по себе не создают новых рисков приватности. Однако при работе с внешними библиотеками и DOM убедитесь, что вы не собираете лишние данные пользователя (например, содержимое полей ввода) и не передаёте их в логи или сторонние сервисы без явного согласия.
Советы по миграции с классовых компонентов
- При миграции на функциональные компоненты
createRef->useRef. - Экспортируйте методы компонента через
forwardRefиuseImperativeHandle, если нужно сохранить API класса:
const FancyInput = React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus()
}));
return ;
});- Переходите постепенно: создайте адаптер-обёртку, которая предоставляет прежний контракт для внешнего кода.
Примеры, когда refs не спасут
- Константная синхронизация данных между несколькими компонентами: используйте state + context.
- Реализация глобального состояния без менеджера состояний: refs усложнят логику и отладку.
Заключение
Refs — это мощный инструмент, позволяющий выйти за пределы декларативной модели React там, где это нужно. Применяйте их сознательно:
- сначала попытайтесь решить задачу декларативно;
- используйте refs для взаимодействия с браузерными API, управлением фокусом, измерениями и интеграцией сторонних библиотек;
- инкапсулируйте DOM-логику, проверяйте
nullи документируйте решения.
Important: чрезмерное использование refs усложняет поддержку приложения и ломает предсказуемое поведение компонентов.
Короткое объявление
Refs в React позволяют получить доступ к созданным DOM-узлам и экземплярам компонентов. Их стоит применять только когда декларативные средства не подходят: управление фокусом, мультимедийное управление, измерения и интеграция внешних библиотек.
Глоссарий в одну строку
- ref: объект с полем
current, указывающим на DOM-узел или экземпляр компонента. - forwardRef: функция для пересылки ref во внутренний элемент.
- useRef: хук для сохранения mutable-значений между рендерами.
- callback ref: функция, которой React передаёт DOM-узел при монтировании.
Похожие материалы
Как быстро включить фонарик на Android
Как делиться экраном в Discord — руководство
Установка Microsoft Teredo в Windows 10
Красный индикатор CPU: причины и исправления
Исправить ошибку xapofx1_5.dll — руководство