forwardRef¶
forwardRef позволяет вашему компоненту передать узел DOM родительскому компоненту с помощью ref.
1 | |
Описание¶
forwardRef(render)¶
Вызовите forwardRef(), чтобы позволить вашему компоненту получить ссылку и переслать ее дочернему компоненту:
1 2 3 4 5 | |
Параметры
render: Функция рендеринга для вашего компонента. React вызывает эту функцию с props иref, которые ваш компонент получил от своего родителя. JSX, который вы возвращаете, будет выходом вашего компонента.
Возвращает
forwardRef возвращает React-компонент, который вы можете отобразить в JSX. В отличие от компонентов React, определяемых как простые функции, компонент, возвращаемый forwardRef, также может принимать пропс ref.
Предостережения
- В строгом режиме React будет вызывать вашу функцию рендеринга дважды, чтобы помочь вам найти случайные примеси. Это поведение только для разработчиков и не влияет на производство. Если ваша функция рендеринга чиста (как и должно быть), это не должно повлиять на логику вашего компонента. Результат одного из вызовов будет проигнорирован.
render функция¶
forwardRef принимает функцию рендеринга в качестве аргумента. React вызывает эту функцию с props и ref:
1 2 3 4 5 6 7 8 | |
Параметры
props: Пропсы, переданные родительским компонентом.ref: Атрибутref, переданный родительским компонентом. Атрибутrefможет быть объектом или функцией. Если родительский компонент не передал атрибут ref, он будетnull. Вы должны либо передать полученныйrefдругому компоненту, либо передать его вuseImperativeHandle.
Возвращает
forwardRef возвращает React-компонент, который вы можете отобразить в JSX. В отличие от компонентов React, определенных как простые функции, компонент, возвращаемый forwardRef, может принимать пропс ref.
Использование¶
Раскрытие узла DOM для родительского компонента¶
По умолчанию DOM-узлы каждого компонента являются приватными. Однако иногда полезно раскрыть узел DOM для родительского компонента - например, чтобы разрешить его фокусировку. Чтобы сделать это, оберните определение вашего компонента в forwardRef():
1 2 3 4 5 6 7 8 9 10 11 | |
Вы получите ref в качестве второго аргумента после props. Передайте его узлу DOM, который вы хотите раскрыть:
1 2 3 4 5 6 7 8 9 10 11 | |
Это позволяет родительскому компоненту Form получить доступ к DOM-узлу <input>, открытому MyInput:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Этот компонент Form передает ссылку в MyInput. Компонент MyInput передает эту ссылку в тег браузера input. В результате компонент Form может получить доступ к узлу DOM input и вызвать focus() для него.
Помните, что раскрытие ссылки на узел DOM внутри вашего компонента усложняет последующее изменение внутреннего устройства компонента. Обычно вы будете раскрывать узлы DOM из многократно используемых низкоуровневых компонентов, таких как кнопки или текстовые входы, но вы не будете делать этого для компонентов прикладного уровня, таких как аватар или комментарий.
Примеры пересылки реферера¶
1. Фокусировка текстового ввода
При нажатии на кнопку происходит фокусировка ввода. Компонент Form определяет ссылку и передает ее компоненту MyInput. Компонент MyInput передает эту ссылку браузеру input. Это позволяет компоненту Form сфокусировать ввод.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
2. Воспроизведение и приостановка видео
Нажатие на кнопку вызывает play() и pause() на DOM-узле <video>. Компонент App определяет ссылку и передает ее компоненту MyVideoPlayer. Компонент MyVideoPlayer пересылает ссылку на узел <video> браузера. Это позволяет компоненту App воспроизводить и приостанавливать <video>.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Пересылка ссылки через несколько компонентов¶
Вместо пересылки ref на узел DOM, вы можете переслать его на свой собственный компонент, например MyInput:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Если компонент MyInput передает ссылку на свой <input>, ссылка на FormField даст вам этот <input>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Компонент Form определяет ссылку и передает ее в FormField. Компонент FormField передает эту ссылку в MyInput, который передает ее в DOM-узел браузера input. Вот как Form получает доступ к этому DOM-узлу.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Выставление императивного дескриптора вместо узла DOM¶
Вместо того чтобы раскрывать весь узел DOM, вы можете раскрыть пользовательский объект, называемый императивным дескриптором, с более ограниченным набором методов. Для этого вам нужно определить отдельный ref для хранения DOM-узла:
1 2 3 4 5 6 7 | |
Передайте полученный ref в useImperativeHandle и укажите значение, которое вы хотите передать ref:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Если какой-то компонент получит ссылку на MyInput, он получит только ваш объект { focus, scrollIntoView } вместо узла DOM. Это позволяет вам ограничить информацию о вашем узле DOM до минимума.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
Подробнее об использовании императивных дескрипторов
Не злоупотребляйте ссылками
Ссылки следует использовать только для императивного поведения, которое вы не можете выразить как пропс: например, прокрутка к узлу, фокусировка узла, запуск анимации, выделение текста и так далее.
Если вы можете выразить что-то как пропс, вы не должны использовать ссылку. Например, вместо того, чтобы раскрывать императивный дескриптор типа { open, close } из компонента Modal, лучше взять isOpen как пропс, например <Modal isOpen={isOpen} />. Effects может помочь вам раскрыть императивное поведение через пропсы.
Устранение неполадок¶
Мой компонент обернут в forwardRef, но ref на него всегда null¶
Обычно это означает, что вы забыли использовать полученный ref.
Например, этот компонент ничего не делает со своим ref:
1 2 3 4 5 6 7 8 9 10 11 | |
Чтобы исправить это, передайте ref вниз к узлу DOM или другому компоненту, который может принимать ссылку:
1 2 3 4 5 6 7 8 9 10 11 | |
Ссылка ref на MyInput может также быть null, если некоторая логика является условной:
1 2 3 4 5 6 7 8 9 10 11 | |
Если showInput будет false, то ссылка не будет перенаправлена ни на какой узел, и ссылка на MyInput останется пустой. Это особенно легко пропустить, если условие скрыто внутри другого компонента, как Panel в этом примере:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Источник — https://react.dev/reference/react/forwardRef