Манипулирование DOM с помощью рефов
React автоматически обновляетDOMв соответствии с результатом рендеринга, поэтому вашим компонентам обычно не нужно им манипулировать. Однако иногда вам может потребоваться доступ к элементам DOM, управляемым React — например, чтобы установить фокус на узел, прокрутить к нему или измерить его размер и положение. В React нет встроенного способа сделать это, поэтому вам понадобитсярефна DOM-узел.
Вы узнаете
- Как получить доступ к DOM-узлу, управляемому React, с помощью атрибута
ref - Как атрибут JSX
refсвязан с хукомuseRef - Как получить доступ к DOM-узлу другого компонента
- В каких случаях безопасно изменять DOM, управляемый React
Получение рефа на узел
Чтобы получить доступ к DOM-узлу, управляемому React, сначала импортируйте хукuseRef:
Затем используйте его для объявления рефа внутри вашего компонента:
Наконец, передайте ваш реф в качестве атрибутаrefтегу JSX, для которого вы хотите получить DOM-узел:
ХукuseRefвозвращает объект с единственным свойством под названиемcurrent. ИзначальноmyRef.currentбудет равенnull. Когда React создаёт DOM-узел для этого<div>, React поместит ссылку на этот узел вmyRef.current. Затем вы сможете получить доступ к этому DOM-узлу из вашихобработчиков событийи использовать встроенныебраузерные API, определённые для него.
Пример: Фокусировка на текстовом поле ввода
В этом примере нажатие на кнопку установит фокус на поле ввода:
Чтобы это реализовать:
- Объявите
inputRefс помощью хукаuseRef. - Передайте его как
<input ref={inputRef}>. Это указывает Reactпоместить DOM-узел этого<input>вinputRef.current. - В функции
handleClickпрочитайте DOM-узел поля ввода изinputRef.currentи вызовите для негоfocus()с помощьюinputRef.current.focus(). - Передайте обработчик события
handleClickв<button>с помощьюonClick.
Хотя манипуляция DOM является наиболее распространённым случаем использования рефов, хукuseRefможет использоваться для хранения других вещей вне React, например, идентификаторов таймеров. Подобно состоянию, рефы сохраняются между рендерами. Рефы похожи на переменные состояния, которые не вызывают повторный рендер при их установке. Подробнее о рефах читайте в разделеСсылки на значения с помощью рефов.
Пример: Прокрутка к элементу
You can have more than a single ref in a component. In this example, there is a carousel of three images. Each button centers an image by calling the browserscrollIntoView()method on the corresponding DOM node:
Доступ к DOM-узлам другого компонента
Подводный камень
Рефы — это аварийный люк. Ручное управление DOM-узламидругогокомпонента может сделать ваш код хрупким.
Вы можете передавать рефы из родительского компонента в дочерние компонентытак же, как и любой другой проп.
В приведённом выше примере реф создаётся в родительском компонентеMyFormи передаётся в дочерний компонентMyInput.MyInputзатем передаёт реф в<input>. Поскольку<input>являетсявстроенным компонентом, React устанавливает свойство.currentрефа в DOM-элемент<input>.
СсылкаinputRef, созданная вMyForm, теперь указывает на DOM-элемент<input>, возвращаемый компонентомMyInput. Обработчик клика, созданный вMyForm, может получить доступ кinputRefи вызватьfocus(), чтобы установить фокус на<input>.
Когда React присваивает ссылки
В React каждое обновление разделено надве фазы:
- Во времярендераReact вызывает ваши компоненты, чтобы определить, что должно быть на экране.
- Во времякоммитаReact применяет изменения к DOM.
Как правило, вамне следуетобращаться к ссылкам во время рендеринга. Это касается и ссылок, содержащих DOM-узлы. Во время первого рендера DOM-узлы ещё не созданы, поэтомуref.currentбудет равенnull. А во время рендера обновлений DOM-узлы ещё не обновлены. Поэтому читать их ещё рано.
React устанавливаетref.currentво время коммита. Перед обновлением DOM React устанавливает значения затронутыхref.current в null. После обновления DOM React немедленно устанавливает их в соответствующие DOM-узлы.
Обычно вы обращаетесь к ссылкам из обработчиков событий.Если вы хотите что-то сделать со ссылкой, но для этого нет конкретного события, вам может понадобиться эффект (Effect). Мы обсудим эффекты на следующих страницах.
Лучшие практики манипуляции DOM с помощью рефов
Рефы — это аварийный люк. Их следует использовать только тогда, когда вам нужно «выйти за пределы React». Типичные примеры включают управление фокусом, позицией прокрутки или вызов браузерных API, которые React не предоставляет.
Если вы ограничитесь недеструктивными действиями, такими как установка фокуса и прокрутка, у вас не должно возникнуть проблем. Однако если вы попытаетесьизменитьDOM вручную, вы рискуете вступить в конфликт с изменениями, которые вносит React.
Чтобы проиллюстрировать эту проблему, этот пример включает приветственное сообщение и две кнопки. Первая кнопка переключает его видимость с помощьюусловного рендеринга и состояния, как обычно делается в React. Вторая кнопка используетDOM API remove(), чтобы принудительно удалить его из DOM вне контроля React.
Попробуйте несколько раз нажать «Toggle with setState». Сообщение должно исчезать и появляться снова. Затем нажмите «Remove from the DOM». Это принудительно удалит его. Наконец, нажмите «Toggle with setState»:
После того как вы вручную удалили DOM-элемент, попытка использоватьsetStateдля его повторного отображения приведёт к сбою. Это происходит потому, что вы изменили DOM, и React не знает, как продолжать управлять им корректно.
Избегайте изменения DOM-узлов, управляемых React.Изменение, добавление или удаление дочерних элементов у узлов, которыми управляет React, может привести к визуальным несоответствиям или сбоям, как показано выше.
Однако это не означает, что этого нельзя делать вообще. Это требует осторожности.Вы можете безопасно изменять части DOM, которые Reactне имеет причинобновлять.Например, если какой-то<div>в JSX всегда пуст, у React не будет причин трогать его список дочерних элементов. Следовательно, там можно безопасно вручную добавлять или удалять элементы.
Итоги
- Рефы — это общая концепция, но чаще всего они используются для хранения DOM-элементов.
- Вы указываете React поместить DOM-узел в
myRef.current, передав<div ref={myRef}>. - Обычно рефы используются для недеструктивных действий, таких как установка фокуса, прокрутка или измерение DOM-элементов.
- Компонент по умолчанию не раскрывает свои DOM-узлы. Вы можете разрешить раскрытие DOM-узла, используя пропс
ref. - Избегайте изменения DOM-узлов, управляемых React.
- Если вы всё же изменяете DOM-узлы, управляемые React, изменяйте только те части, которые React не имеет причин обновлять.
Try out some challenges
Challenge 1 of 4:Play and pause the video #
In this example, the button toggles a state variable to switch between a playing and a paused state. However, in order to actually play or pause the video, toggling state is not enough. You also need to call play() and pause() on the DOM element for the <video>. Add a ref to it, and make the button work.
For an extra challenge, keep the “Play” button in sync with whether the video is playing even if the user right-clicks the video and plays it using the built-in browser media controls. You might want to listen to onPlay and onPause on the video to do that.
