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
проп.
Застереження
- У строгому режимі 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}>
Редагувати
</button>
</form>
);
}
Цей компонент Form
передає реф до MyInput
. Компонент MyInput
направляє той реф до браузерного тегу <input>
. Як результат, компонент Form
має доступ до DOM-вузла <input>
та може викликати focus()
на ньому.
Пам’ятайте, що розкриття рефу DOM-вузла всередині вашого компонента робить важчою зміну внутрішніх частин компонента пізніше. Зазвичай, ви будете розкривати DOM-вузли з компонентів нижнього рівня, що перевикористовуються (як-от кнопки та поля вводу), та не будете робити це із глобальними компонентами, такими як аватар чи коментар.
Example 1 of 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}> Редагувати </button> </form> ); }
Передача рефу через кілька компонентів
Замість направлення ref
до 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}>
Редагувати
</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}> Редагувати </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(); // Це не працюватиме, тому що DOM-вузол не розкритий: // ref.current.style.opacity = 0.5; } return ( <form> <MyInput label="Enter your name:" ref={ref} /> <button type="button" onClick={handleClick}> Редагувати </button> </form> ); }
Прочитайте більше про використання імперативного керування.
Усунення неполадок
Мій компонент обгорнутий у forwardRef
, але ref
до нього завжди 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>
);
});
ref
до 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>
);
});