Refs и DOM

f

Почему Refs — это не просто «ссылка на элемент»

Вы уже освоили основы React и теперь чувствуете, что Refs — это удобный способ достучаться до DOM напрямую. Но здесь кроется первая ловушка: многие разработчики воспринимают ref как магическую палочку для любого «низкоуровневого» доступа. На самом деле, если вы слишком часто тянетесь к ref, это сигнал: что-то не так с архитектурой компонента. Вы начнёте замечать, как компоненты становятся менее декларативными, а тесты — хрупкими.

Представьте: вы делаете анимацию, фокусируете поле, работаете с медиаэлементом. В этих случаях ref — ваш друг. Но когда вам нужно получить значение input для отправки формы — используйте управляемые компоненты, иначе вы потеряете контроль над потоком данных. Профессионалы смотрят на ref как на аварийный выход, а не на парадную дверь.

Типичные проблемы: что идёт не так

Первый камень преткновения — синхронизация. Вы создали ref через useRef, передали в DOM-элемент, но в момент первого рендера ref равен null. Это не баг, это особенность жизненного цикла. Если вы попытаетесь прочитать node.current сразу после инициализации, то ничего не получите — узел ещё не смонтирован. Такое поджидает новичков при работе с useEffect и ref одновременно.

Вторая проблема — утечка производительности. Если вы используете ref в условном рендеринге и каждый раз пересоздаёте его в корне компонента, при перерендерах теряется ссылка на предыдущий DOM-элемент. Это приводит к «морганию» фокуса или сбросу состояния анимации. Специалисты всегда проверяют, не меняется ли ref при изменении пропсов, и при необходимости фиксируют его в useRef с начальным значением null.

Нюанс с forwardRef: передаём ref глубже

Когда вы пишете собственный компонент-обёртку, возникает вопрос: как передать ref вниз, к конкретному DOM-узлу? forwardRef существует именно для этого, но у него есть неочевидный эффект. Если ваш компонент использует ref только для сторонней библиотеки (например, для кастомного скроллбара), не стоит форвардить ref на каждый элемент внутри — это делает API компонента размытым. Вместо этого создайте специальный интерфейс с методами scrollTo или focus, доступными через ref.

Ещё один подводный камень: при использовании forwardRef с TypeScript приходится явно типизировать и ref, и компонент. Часто видели код, где ref объявлен как any — так вы теряете все преимущества статической проверки. Потратьте 10 минут на аккуратную типизацию, и IDE будет подсказывать вам методы DOM-элемента, а не просто «Object».

Работа с несколькими элементами: коллекция ref

Бывает, нужно создать набор ref для списка элементов — например, для фокусировки на следующем поле формы при нажатии Enter. Простейший способ — хранить массив рефов в useRef, где каждый элемент массива — новый createRef() или null. Но внимание: если динамически удаляете элементы из списка, массив пересоздаётся, и ref для удалённого элемента должен быть правильно очищен, иначе React попытается вызвать метод у уже unmounted элемента.

Профессиональный приём: используйте callback ref. Вы передаёте функцию, которая вызывается при каждом изменении узла (mount/unmount). Она принимает DOM-узел и автоматически присваивает его вашей переменной. Это даёт гибкость — можно управлять созданием и удалением ref без массивов. Плюс callback ref корректно обрабатывает асинхронные обновления списка.

Проблема «запаздывающего» DOM

Вы обновляете state и тут же обращаетесь к DOM через ref, ожидая, что элемент уже изменился. Но React применяет изменения асинхронно — между setState и фактическим обновлением DOM проходит микротаск. Чтобы избежать чтения старого DOM, используйте useLayoutEffect: он выполняется синхронно до того, как браузер отрисует изменения. Этим приёмом пользуются специалисты для анимаций, позиционирования тултипов и работы с шириной/высотой флекс-контейнеров.

Если же вы просто читаете значения (например, scrollTop), и разница в один кадр не критична, используйте useEffect — это безопаснее и производительнее. Помните: лишняя синхронизация в useLayoutEffect может привести к подёргиваниям интерфейса.

Когда ref — зло, а когда — спасение

Профессиональный чек-лист работы с DOM через ref

Как отучить себя злоупотреблять ref

Часто причиной использования ref становится простое незнание альтернатив. Например, чтобы изменить класс элемента по клику, вы тянетесь к element.classList через ref. Вместо этого добавьте в state флаг и используйте условный класс в JSX. Это сохранит паттерн «сверху вниз» и упростит отладку. Специалисты советуют: перед тем как взять ref, напишите ответ на вопрос «Могу ли я этого добиться через props?».

И ещё один жёсткий приём: ограничьте себя одним ref на компонент. Если не помещается — значит, компонент делает слишком много. Разбейте его на дочерние, передавайте состояние через props. В результате вы получите более переиспользуемый код, который легче тестировать и поддерживать.

В итоге, когда вы перестанете рассматривать ref как «просто ссылку» и начнёте видеть в нём инструмент с чёткими границами применения, ваш код станет чище и надёжнее. Вы научитесь отличать ситуации, где ref — это правильный выбор, от тех, где он маскирует плохую архитектуру. И запомните: профессиональный React-разработчик знает, что 80% случаев использования ref можно заменить лучшим решением. А оставшиеся 20% — это именно то, где ref незаменим.

Добавлено: 23.04.2026