Что такое refs в react
3.3 Ссылки ref и DOM
Ссылки предоставляют способ доступа к узлам DOM или элементам React, созданным в методе отрисовки(render).
В типичном потоке данных React, свойства props – это единственный способ взаимодействия родительского компонента с его потомком. Чтобы модифицировать потомка, вы перерисовываете его с новыми свойствами. Тем не менее, есть несколько случаев, когда вам необходимо модифицировать потомок вне обычного потока данных. Потомок, подлежащий модификации, может быть экземпляром React-компонента или являться DOM-элементом. Для обоих этих случаев, React предоставляет «запасной выход».
3.3.1 Когда использовать ссылки ref
Существует несколько оправданных случаев использования ссылок ref :
Избегайте использования ссылок для всего, что может быть реализовано декларативным путем!
3.3.2 Не злоупотребляйте ссылками ref
По началу вы можете быть склонны использовать ссылки ref для того, чтобы «достигнуть результата» в вашем приложении. Если это так, то возьмите немного времени и подумайте более критично о том, кто должен владеть состоянием в иерархии компонентов. Часто, становится понятно, что правильное место, где должно находиться состояние, это более высокий уровень в иерархии. Смотрите главу «Передача состояния вверх по иерархии» в качестве примера.
3.3.3 Создание ссылок
3.3.4 Доступ к ссылкам
Значение ref отличается в зависимости от типа узла:
Приведенные ниже примеры демонстрируют эти различия.
3.3.4.1 Добавление ссылки ref на DOM-элемент
Данный код использует ref для хранения ссылки на узел DOM:
3.3.4.2 Добавление ссылки ref на компонент-класс
Если бы мы захотели обернуть компонент CustomTextInput выше, чтобы имитировать нажатие по нему сразу после монтирования, мы могли бы использовать атрибут ref для доступа к этому компоненту и вручную вызвать его метод focusTextInput() :
Обратите внимание, что это работает только в том случае, если CustomTextInput объявлен как класс:
3.3.4.3 Ссылки ref и функциональные компоненты
Нельзя использовать атрибут ref на компонентах-функциях, так как они не имеют экземпляров:
Вы должны преобразовать компонент в класс, если хотите ссылаться на него. Точно так же вы делаете, когда вам необходимо наделить компонент методами жизненного цикла и состоянием.
Вы, тем не менее, можете использовать атрибут ref внутри функционального компонента, так как вы ссылаетесь на DOM-элемент или класс компонента:
3.3.5 Предоставление ссылок на DOM-узлы родительским компонентам
В редких случаях, вы можете захотеть получать доступ к DOM-элементам потомков из родительского компонента. Как правило, это не рекомендуется, так как разрушает инкапсуляцию компонента. Но изредка это может быть полезным для переключения фокуса, определения размера или позиции DOM-элемента потомка.
В то время как вы имеете возможность добавлять ссылку на компонент потомка, это не является идеальным решением, так как в коллбэке атрибута ref вы можете получить только экземпляр компонента, а не DOM-узел. Вдобавок, это не будет работать с функциональными компонентами.
Если вы используете React 16.3 и выше, для таких случаев мы рекомендуем использовать передачу ссылок. Передача ссылок дает компонентам свободу выбора, в предоставлении любых ссылок своих из потомков. Вы можете найти подробный пример того, как предоставить дочерний DOM-узел родительскому компоненту в документации по передаче ссылок.
Если вы используете React 16.2 и ниже, или если вам нужна большая гибкость, чем предоставленная передачей ссылок, вы можете использовать данный альтернативный подход и явно передать ссылку как свойство с другим именем.
3.3.6 Ref-коллбэки
React также поддерживает и другой способ установки ссылок ref, называемый «callback refs» или «ref-коллбэки», который дает более гибкий контроль в моменты, когда ссылки установлены и не установлены.
В приведенном ниже примере реализован общий паттерн: использование обратного вызова в атрибуте ref для сохранения ссылки на узел DOM в свойстве экземпляра.
3.3.7 Старый API: строковые ссылки ref
3.3.8 Предостережения
Анатомия React. Урок 12. Что такое ref’ы и как ими пользоваться?
Разбираемся с ref’ами, которые позволяют добираться до конкретных DOM-нод и производить с ними различные манипуляции.
Давайте рассмотрим так называемые ref’ы. На самом деле это просто сокращение от английского reference — ссылка.В сущности они просто позволяют получить доступ к тем нодам внутри компонента, которые были созданы в результате выполнения метода render().
Это бывает нужно по разным причинам:- при работе с API для выделения текста или с фокусом текстовых полей,- произведения каких-то анимаций, перемещений и изменений внешнего вида путем прямого взаимодействии с нодой (хотя рекомендуется маскимально этого не делать), замеров размеров DOM-нод,- при работе с внешними библиотеками — скажем с jQuery (хотя уж его точно не стоит использовать в современных React-приложениях).
Поначалу новички, особенно те, кто перешел с jQuery на React могут злоупотреблять использованием рефов, однако со временем все становится на свои места.
Итак, исторически ref’ы в React прошли эволюционный путь, состоящий из трех этапов.
Ref как строка
Сначала ref’ы задавались строкой. Сразу замечу, что этот синтаксис устаревший, так что никогда его не используйте в современных приложениях. А выглядело это так:
Здесь у целевой ноды, к которой мы хотим обратиться, стоит специальный атрибут ref и его значение — строка. А эта строка фигурирует и вот здесь — в методе handleSubmit, срабатывающем когда мы отправляем форму. То есть this.refs это специальное поле в нашем инстансе компонента, которое хранит все подобные рефы, которые определены внутри этого компонента. Оттуда к ним доступ и осуществляется.
Ref как callback
Следующим этапом стала передача функции в атрибут ref.
Здесь в ref передается уже не строка, а колбэк, который после своего выполнения (после первого рендера), в this.textInput сохраняет ссылку на нативную браузерную DOM-ноду, к которой в componentDidMount мы и обращаемся вызывая метод focus. Это, после того как компонент замаунтился, заставляет его поймать фокус.
Вы можете быть уверены, что ДО выполнения методов жизненного цикла componentDidMount и componentDidUpdate, ref’ы уже будут установлены.
Ref как вызов createRef
Ну и наконец самым современным и рекомендуемым способом является вызов в конструкторе метода React.createRef():
Тут вообще все просто. Вызов обозначенной выше функции создает в this.inputRef структуру данных, которая пердназначена для хранения ref’а. А в render в ref передается ссылка на эту структуру в классе компонента. И вот, ref уже готов к использованию. Пишем this.inputRef.current и получаем ссылку на DOM-ноду.
Видно, что в консоли после выполнения componentDidMount появилось содержимое инпута.
Еще момент — мы можем в дочерний компонент передать ref как один из пропсов, который будет называться иначе чем ref, и затем уже в дочернем установить его на конкретный элемент, чтобы получить из родителя доступ к ноде дочернего элемента.
Данный код демонстрирует, как колбэк, устанавливающий в Parent ссылку на DOM-ноду мы можем отправить в дочерний компонент. Это происходит вот здесь, в пропсе setTextInputRef. Этот колбэк мы отправляем в пропс ref компонента CustomTextInput и так как этот колбэк объявлен в родительском компоненте, то и ссылка на DOM-ноду установится там же. Соответственно, после componentDidMount инпут нормально зафокусится. Как и в примере выше.
Фактически, мы сейчас кастомизировали инпут и заставили его работать так, будто бы он находится прямо в компоненте Parent.
А вот еще более интересный пример:
Тут мы пропс ref пишем прямо у компонента, а не у известного браузеру HTML-элемента. И это работает иначе.
Компонент Parent уже рассмотренным ранее способом готовит в конструкторе поле для ref’а, затем в рендере ссылка создается. И если посмотреть, то она будет указывать на React-компонент, а не на DOM-ноду как в предыдущих случаях. Вот это и есть преимущество, так как получив такую ссылку, мы можем у CustomTextInput вызывать его методы. По нажатию на кнопку фокуса такой метод focusInput у CustomTextInput как раз и вызывается из Parent. А CustomTextInput уже внутри себя имеет ref на конкретный HTML-элемент инпут и может через this.inputRef.current вызвать нативный браузерный метод focus. И вот, наша задача достигнута.
Замечу, что такое не пройдет с компонентами, объявленными как функции, в отличие от тех, что объявлены в качестве класса. Такое просто-напросто не будет работать. Ведь нет инстанса и не у кого вызывать метод.
Но если же компонент у вас все же объявлен как функция, в него можно пробросить другую функцию, устанавливающую ref в классе родителя и работать с этим так же. Это было уже рассмотрено ранее.
Вообще же, в компоненте, объявленном как функция, можно использовать ref’ы:
Вместо использования конструктора мы просто создаем переменные — с инициализацией структуры под ref и с обработчиком клика по кнопке.
Ну и давайте еще вспомним для полноты картины про метод ReactDOM.findDOMNode. Он получает в качестве аргумента React-компонент и позволяет покопаться в DOM-содержимом его инстанса (ну или экземпляра — напоминание для тех кто еще не запомнил, что это одно и то же).
Я модифицировал предыдущий пример и добавил в обработчик клика кнопки Focus вывод в консоль результата работы ReactDOM.findDOMNode, которой я передал ссылку на наш компонент CustomTextInput, а точнее на его инстанс — не забываем про current в конце — и это дает нам DOM-ноду, с которой можно делать все, что позволено в браузере делать с DOM-нодой.
Например, давайте поменяем её текст. Вот так — взяли её инстанс, поискали по классу с помощью querySelector и поменяли innerText. Результат вы видите на экране.
Но правда лучше использовать эту штуку в самом крайнем случае, когда ну ничего уже не помогает. В специальном ограниченном режиме Strict Mode реакта, это работать перестанет. Про Strict мы еще поговорим.
Теперь про так называемый Ref Forwarding. О чем идет речь? Рассмотрим такую кнопку:
Рисуется обычный button и то, что внутри него, соответственно также отрисуется благодаря props.children — нам уже это известно. По-хорошему, один компонент не должен вмешиваться в разметку другого и менять стили или тем более структуру его дерева элементов. Но это больше справедливо для высокоуровневых компонентов вроде комментариев или календаря какого-нибудь. Для простых же компонентов вроде стилизованной кнопки или инпута, это удобно, чтобы изменить фокус, допустим. Или как-то анимировать. Или выделить текст.
И вот тут-то Forwarding Ref приходятся как нельзя кстати. Да, по-русски это можно перевести как «пробрасываемые рефы».
Данный пример наглядно демонстрирует особенности пробрасываемых ref’ов. С помощью метода React.forwardRef мы создаем компонент нашей кнопки и в него пробрасываются не только пропсы, а еще и ref, который для нас эта обертка и создала. Далее этот реф пробрасывается в button.
Затем, когда мы используем в другом компоненте нашу кастомную кнопку, то пробрасываем ей ref, который создаем уже привычным способом. При маунтинге компонента App, наша кнопка прекрасно получает фокус. Все работает.
Эта техника хорошо применима в компонентах высшего порядка (higher-order components) — я их называю просто и удобно — ХОК — от соответствующего акронима.
Сейчас будет, возможно, немного запутанный для вас пример. Необходимо в него вникнуть хорошо, если хотите все до конца понять.
index.js:
App.js:
LogProps.js (неработающий вариант):
LogProps.js (работающий вариант):
CustomBtn.js:
В index.js просто рисуется компонент App.
App рисует CustomBtn с надписью SENDING внутри, благодаря props.children. Тут же, создается ref и отправляется в пропс ref компонента CustomBtn. Как только App маунтится, он через этот реф у CustomBtn вызывает метод changeSendingStatus.
Дальше самое интересное. HOC-компонент LogProps. Его назначение, выводить пропсы любого компонента, который им обернут. В нашем случае CustomBtn как раз имеет обертку в виде logProps. И мы для начала посмотрим на неработающий вариант и поймем почему он не работает.
Тут у нас объяалена функция logProps с маленькой l. В нее пробрасывается тот, компонент, который нужно обернуть. Внутри объявляется класс LogProps с большой L. Он рендерит WrappedComponent, а это в нашем случае будет CustomBtn, и пробрасывает в него все пропсы, которые были проброшены этажом выше. Как только данный компонент-обертка маунтится, он все пропсы выводит — как предыдущие, так и текущие. И компонент LogProps возвращается из функции. Почему — сейчас поймем.
Оборачиваемый компонент CustomBtn. Вот тут, в экспорте, мы нашу кнопку оборачиваем заимпорченным logProps.
Смотрим еще раз на цепочку. При экспорта выполняется функция logProps, получая аргументом наш CustomBtn. Тут он уже называется WrappedComponent и возвращается из компонента LogProps, а уже компонент LogProps, завязанный на WrappedComponent, возвращается из функции logProps. И попадает в экспорт из CustomBtn. Вот так и получается что отрисованный в App.js CustomBtn уже обернут в logProps.
Но скользкий момент в том, что несмотря на то, что мы передали в CustomBtn ref, он передается в компонент обертку и не пробрасывается дальше, в сам CustomBtn, так как расспредивание this.props не затрагивает специальный реактовский пропс ref и он в этот список просто не включается. При попытке запустить код в этом случае, мы увидим, что ref в App.js будет указывать на LogProps. В этом-то и загвоздка.
И здесь на помощь приходит как раз ref forwarding. Вот измененный код logProps. Вот тут, вместо того, чтобы просто возвратить компонент LogProps, мы возвращаем результат выполения React-метода forwardRef, который получает аргументом колбэк. Туда пробрасываются пропсы и ref, тот самый, который ранее нам был недоступен. Пропсы деструктурируются отдельно, а ref отправляется в отдельный пропс forwardedRef. И тогда внутри LogProps в методе render, мы можем из его уже пропсов извлечь отдельно forwardedRef и остальные пропсы. forwardedRef попадает в ref, а остальные пропсы деструктурируются отдельно.
И вот теперь явно видно, что в App.js ref указывает на CustomBtn. Внутри него создается свой ref, который указывает на button и метод changeSendingStatus, где меняется текст кнопки на SENT. Кстати, плохой пример, потому что по идее текст внутри зависит от this.props.children, а тут прямое вмешательство с перезаписью этого текста. В общем, вы поняли как не надо делать =)
Кстати, смотрите как выглядит React-дерево для этого примера. Вся наша структура дополнительно обернулась еще и в ForwardRef. Мы можем вместо стрелочной безымянной функции пробросить сюда функцию с именем:
Тогда в скобочках у ForwardRef это имя появится. Удобно для отладки.
А можно сделать еще круче:
Выносим данную функцию отдельно, назвав, например, forwardRef. Затем формируем имя оборачиваемого компонента в переменной name. Для этого берем у него значение displayName или name, если первого нет. дальше приписываем logProps и в скобках интерполируем name. И наконец, вызываем React.forwardRef с «пропатченной» функцией forwardRef.
Что ж.. Надеюсь материал данного урока не поплавил ваш мозг. Если так, то пересмотрите это видео несколько раз с промежутком в несколько дней, попутно запуская самостоятельно, а лучше набирая ручками, все примеры из урока. Всем удачи и встретимся на следующем занятии по React’у. Будет еще интереснее.
Персональные уроки по скайпу от MakeWeb
Avocode — замена Photoshop для верстальщика
При регистрации используй инвайт «nikita» и получи скидку 10% на первую покупку!Работа с ref в React
Дата публикации: 2018-09-17
От автора: React ref позволяет получить доступ к узлам DOM непосредственно внутри React. Это полезно в ситуациях, когда, например, вы хотите изменить дочерний элемент компонента. Предположим, вы хотите изменить значение элемента input, но без использования свойства или повторного рендеринга всего компонента. Это то, что мы рассмотрим в этом посте.
Как создать ref
createRef() — это новый API, который поставляется с React 16.3. Вы можете создать ref, вызвав React.createRef() и связав с ним элемент React, используя атрибут ref для элемента.
Мы можем «ссылаться» на узел ref, созданный в методе рендеринга, с доступом к текущему атрибуту ref. В приведенном выше примере это будет this.exampleRef.current. Вот еще один пример:
Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения
value
Как может происходить общение между дочерним компонентом и элементом, содержащим ref.
Это компонент, который отображает текст, поле ввода и кнопку. Параметр ref создается в конструкторе и затем привязывается к элементу ввода при его рендеринге. Когда кнопка нажата, значение, представленное элементом ввода (с привязанным к нему ref), используется для обновления состояния текста (содержащегося в теге H3). Мы используем this.textInput.current.value для доступа к значению, и новое состояние затем отображается на экране.
Передача функции обратного вызова в ref
React позволяет создать ref, передав функцию обратного вызова атрибуту ref компонента. Вот как это выглядит:
Обратный вызов используется для хранения ссылки на узел DOM в свойстве экземпляра. Когда мы хотим использовать эту ссылку, мы получаем доступ к ней, используя:
Посмотрите, как это выглядит в том же примере, который мы использовали ранее.
Когда вы используете обратный вызов, как мы это делали выше, React выполнит обратный вызов ref с узлом DOM, когда компонент будет монтироваться; когда компонент будет размонтироваться, он будет назван с нулем. Также возможно передать ref из родительского компонента в дочерний, используя обратные вызовы.
Давайте создадим наш «немой» компонент, который выводит простые введенные данные:
Этот компонент ожидает реквизиты inputRef из своего родительского компонента, которые затем используются для создания ссылки на узел DOM. Вот родительский компонент:
В компоненте App мы хотим получить текст, который вводится в поле ввода (которое находится в дочернем компоненте), чтобы мы могли его отобразить. Ссылка создается с помощью обратного вызова, как в первом примере этого раздела. Ключевым моментом является то, как мы получаем доступ к DOM элемента ввода в компоненте Input из компонента App. Если вы внимательно посмотрите код, мы обращаемся к нему с помощью this.inputElement. Таким образом, при обновлении состояния значения в компоненте App мы получаем текст, который был введен в поле ввода, используя this.inputElement.value.
Атрибут ref как строка
Это старый способ создания ссылки, и, скорее всего, он будет удален в будущей версии из-за некоторых связанных с ним проблем. Команда React советует не использовать его, поскольку он помечен в документации, как «устаревший». Мы все равно включаем его в эту статью, потому что есть шанс, что вы встретите его в базе кода.
Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения
Возвращаясь к нашему примеру элемента ввода, значение которого используется для обновления передаваемого текстового значения:
Инициализируется компонент, и мы начинаем со значения по умолчанию — пустой строки (value = »). Компонент отображает текст и форму, как обычно, и, как и раньше, текст H3 обновляет состояние, когда форма отправляется с содержимым, введенным в поле ввода.
Мы создали ref, установив для свойства ref поля ввода — textInput. Это дает нам доступ к значению элемента ввода в методе handleSubmit(), используя this.refs.textInput.value.
Пересылка ref от одного компонента в другой
** Пересылка ref — это метод передачи ref от компонента к дочернему компоненту с использованием метода React.forwardRef ().
Вернемся к нашему примеру поля ввода, которое обновляет значение текста при его отправке:
В этом примере мы создали ref со значением inputRef, и хотим передать его дочернему компоненту в качестве атрибута ref, который мы можем использовать для обновления состояния текста.
Вот альтернативный способ сделать это, указав ref вне компонента App:
Использование ref для валидации формы
Мы все знаем, что валидация формы — это что-то очень сложное, но это то, с чем отлично справляется React. Вы прекрасно знаете о таких вещах, как проверка заполнения обязательных полей. Или проверка наличия в пароле, как минимум, шести символов. Refs может использоваться и в этих случаях.
Мы использовали createRef() для создания ref для поля ввода и передали его в качестве параметра методу валидации. Мы заполняем массив ошибок, когда в одном из полей ввода возникает ошибка, которую мы затем отображаем пользователю.
Вот и все о ref!
Надеюсь, это пошаговое руководство дало вам представление о том, насколько мощными может быть ref. Это отличный способ обновить часть компонента без необходимости повторной обработки. Это удобно для написания более компактного кода и повышения производительности.
В то же время стоит прислушаться к советам документации самого React и не использовать ref слишком часто:
«Первым признаком того, что вам стоит использовать ref, может быть ситуация, когда вам нужно «заставить, чтобы что-то случилось» в вашем приложении. В этом случае, подумайте на тем, какому компоненту в иерархии компонентов должно принадлежать состояние. Часто становится становится понятно, что это должен быть компонент более высокого уровня в иерархии».
Вам все понятно? Отлично.
Автор: Kingsley Silas
Редакция: Команда webformyself.
Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения
ReactJS: основы
Изучите основы ReactJS и создайте ваше первое приложение на ReactJS
Всем привет. В этом уроке мы разберем с вами что такое refs в React. В прошлых уроках мы разобрали с вами два типа данных в React: props и state. Нам осталось разобрать еще один тип данных в React. Это refs. Он позволяет обращатся к конкретному DOM елементу компонента.
Давайте попробуем. Не так давно в React изменили синтаксис работы с refs и теперь он вот такой.
Давайте создадим input и button
Если мы посмотрим в браузер, то увидим, что наша форма отрисовалась.
Добавим атрибут onClick и прибайндим метод this.submit в нашему контексту.
Для этого навешиваем onClick евент
и добавляем submit метод
Если мы посмотрим в браузер, то наша функция submit вывела консоль лог при клике.
Теперь для того, чтобы получить значение DOM елемента input нужно навесить на него вот такую конструкцию.
Здесь мы указали атрибут ref, который является функцией и присваивает input в переменную this.testInput. React вызвает ref callback, когда наш компонент рендерится. Это обычный паттерн в React как получить доступ к DOM елементам.
Теперь в нашей функции submit мы можем вывести this.testInput.
Как мы видим, в консоль нам вывелся DOM елемент. Теперь мы можем делать с ним что угодно. Например, узнать его значение в данный момент.
Если мы введем что-то в input и нажмем submit, то в консоль выведется значение input.
Ref отличный способ доступа к DOM елементам, но его нужно применять с осторожностью. Нужно помнить, что это не React way, а просто возможность доступа к DOM елементам. Всегда лучше использовать state, props если это возможно вместо refs, так как они поддерживают правильный флоу данных в приложении, а refs нет.
Перенаправление рефов
Перенаправление рефов позволяет автоматически передавать реф компонента одному из его дочерних элементов. Большинству компонентов перенаправление рефов не нужно, но оно может быть полезно, например, если вы пишете библиотеку. Рассмотрим наиболее частые сценарии.
Перенаправление рефов в DOM-компоненты
React-компоненты скрывают свои детали реализации, в том числе результат рендеринга. Реф элемента button из FancyButton обычно и не требуется другим компонентам. Это хорошо, поскольку такой подход не даёт компонентам излишне полагаться на структуру DOM друг друга.
Перенаправление рефов позволяет взять ref из атрибутов компонента, и передать («перенаправить») его одному из дочерних компонентов.
Рассмотрим этот пример пошагово:
Перенаправить реф можно не только в DOM-компонент, но и в экземпляр классового компонента.
Примечание для разработчиков библиотек компонентов
Если вы впервые использовали forwardRef в компоненте библиотеки, то следует сделать новую версию мажорной и указать на обратную несовместимость изменений. Причина этого в том, что, скорее всего, компонент станет вести себя заметно иначе (например, изменится тип экспортируемых данных и элемент, к которому привязан реф), в результате чего приложения и другие библиотеки, полагающиеся на старое поведение, перестанут работать.
По этой же причине мы рекомендуем не вызывать React.forwardRef условно (то есть сперва проверяя, что эта функция определена). Это изменит поведение вашей библиотеки и приложения ваших пользователей могут перестать работать при обновлении самого React.
Перенаправление рефов в компонентах высшего порядка
Особенно полезным перенаправление может оказаться в компонентах высшего порядка (также известных как HOC). Начнём с примера, в котором HOC выводит пропсы компонента в консоль:
Компонент высшего порядка logProps передаёт все пропсы в компонент, который он оборачивает, так что рендерить они будут одно и то же. С его помощью мы будем выводить в консоль все пропсы, переданные в наш компонент с кнопкой:
Изменение названия в инструментах разработки
В React.forwardRef передаётся функция рендеринга. Эта функция определяет, как будет называться компонент в инструментах разработки.
Например, вот этот компонент будет называться «ForwardRef»:
Если присвоить имя функции рендеринга, то оно появится в названии компонента в инструментах разработки (например, «ForwardRef(myFunction)»):
Можно даже назначить функции свойство displayName и указать в нём, какой именно компонент обёрнут в HOC: