forwardRef
forwardRef позволяет вашему компоненту экспонировать узел DOM для родительского компонента с помощью ссылкой.
const SomeComponent = forwardRef(render)
Ссылка
forwardRef(render)
Вызовите forwardRef(), чтобы ваш компонент получал ссылку и передавал её дочернему компоненту:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
// ...
}); См. дополнительные примеры ниже.
Параметры
-
render: Функция отрисовки вашего компонента. React вызывает эту функцию с пропсами иref, которые ваш компонент получил от родительского. JSX, который вы возвращаете, будет выводом вашего компонента.
Возвращаемое значение
forwardRef возвращает React-компонент, который вы можете отобразить в JSX. В отличие от React-компонентов, определённых как обычные функции, компонент, возвращённый forwardRef, также может получать пропс ref.
Замечания
- В режиме Strict Mode React вызовет вашу функцию отрисовки дважды, чтобы помочь обнаружить случайные ошибки. Это поведение только для разработки и не влияет на производство. Если ваша функция отрисовки является чистой (как это и должно быть), это не должно повлиять на логику вашего компонента. Результат одного из вызовов будет проигнорирован.
render функция
forwardRef принимает функцию отрисовки в качестве аргумента. React вызывает эту функцию с props и ref:
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
}); Параметры
-
props: Пропсы, переданные родительским компонентом. -
ref: Атрибутref, переданный родительским компонентом.refможет быть объектом или функцией. Если родительский компонент не передал ссылку, она будетnull. Вы должны либо передать полученнуюrefдругому компоненту, либо передать её вuseImperativeHandle.
Возвращаемое значение
forwardRef возвращает React-компонент, который вы можете отобразить в JSX. В отличие от React-компонентов, определённых как обычные функции, компонент, возвращённый forwardRef, может принимать пропс ref.
Использование
Экспонирование узла DOM для родительского компонента
По умолчанию узлы DOM каждого компонента являются закрытыми. Однако иногда бывает полезно экспонировать узел DOM для родителя — например, для возможности его фокусировки. Для этого оберните определение компонента в forwardRef():
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
}); Вы получите ссылку в качестве второго аргумента после пропсов. Передайте её узлу DOM, который вы хотите экспонировать:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
}); Это позволяет родительскому Form компоненту получить доступ к <input> узлу DOM , экспонированному MyInput:
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
} Этот Form компонент передаёт ссылку в MyInput. Компонент MyInput передаёт эту ссылку тегу <input> браузера. В результате, компонент Form может получить доступ к этому <input> узлу DOM и вызвать focus() на нём.
Помните, что экспонирование ссылки на узел DOM внутри вашего компонента затрудняет последующие изменения внутренней части вашего компонента. Обычно вы экспонируете узлы DOM из повторно используемых компонентов низкого уровня, таких как кнопки или текстовые поля, но не будете делать это для компонентов уровня приложения, таких как аватар или комментарий.
Примеры передачи ссылки
Пример 1 из 2:
Фокусировка текстового поля
Нажатие на кнопку сфокусирует поле ввода. Компонент Form определяет ссылку и передаёт её компоненту MyInput. Компонент MyInput передаёт эту ссылку на тег <input> браузера. Это позволяет компоненту Form сфокусировать <input>.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> <MyInput label="Enter your name:" ref={ref} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
Передача ссылки через несколько компонентов
Вместо передачи ссылки на узел DOM, вы можете передать её своему компоненту, например, MyInput:
const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
}); Если этот MyInput компонент передаёт ссылку своему <input>, ссылка на FormField предоставит вам эту <input>:
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
} Компонент Form определяет ссылку и передаёт её FormField. Компонент FormField передаёт эту ссылку MyInput, который передаёт её узлу DOM <input> браузера. Вот как Form получает доступ к этому узлу DOM.
import { useRef } from 'react'; import FormField from './FormField.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); } return ( <form> <FormField label="Enter your name:" ref={ref} isRequired={true} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
Экспонирование императивного обработчика вместо узла DOM
Вместо экспонирования всего узла DOM, вы можете экспонировать настраиваемый объект, называемый императивным обработчиком, с более ограниченным набором методов. Для этого вам потребуется определить отдельную ссылку для хранения узла DOM:
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
// ...
return <input {...props} ref={inputRef} />;
}); Передайте полученную ref в useImperativeHandle и укажите значение, которое вы хотите экспонировать для ref:
import { forwardRef, useRef, useImperativeHandle } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
}); Если какой-то компонент получит ссылку на MyInput, он получит только ваш объект { focus, scrollIntoView } вместо узла DOM. Это позволяет ограничить информацию, которую вы экспонируете об узле DOM, до минимума.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); // This won't work because the DOM node isn't exposed: // ref.current.style.opacity = 0.5; } return ( <form> <MyInput placeholder="Enter your name" ref={ref} /> <button type="button" onClick={handleClick}> Edit </button> </form> ); }
Дополнительная информация об использовании императивных обработчиков.
Возможная ошибка
Не злоупотребляйте ссылками. Вам следует использовать ссылки только для императивного поведения, которое нельзя выразить через пропсы: например, прокрутка к узлу, фокусировка узла, запуск анимации, выбор текста и так далее.
Если вы можете выразить что-то через пропс, не используйте ссылку. Например, вместо экспонирования императивного обработчика, такого как { open, close } от компонента Modal, лучше принять isOpen в качестве пропса, как в <Modal isOpen={isOpen} />. Эффекты могут помочь вам экспонировать императивное поведение через пропсы.
Отладка
Мой компонент обернут в forwardRef, но ссылка на него всегда null
Обычно это означает, что вы забыли использовать полученную ref.
Например, этот компонент ничего не делает со своей ref:
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
}); Для исправления передайте ref в узел DOM или другой компонент, который может принять ссылку:
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
}); Ссылка на MyInput также может быть null , если часть логики является условной:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
}); Если showInput является false, ссылка не будет передана ни одному узлу, а ссылка на MyInput останется пустой. Это особенно легко пропустить, если условие скрыто внутри другого компонента, например, Panel в этом примере:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});
© 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License.
https://18.react.dev/reference/react/forwardRef