| title | useImperativeHandle |
|---|
useImperativeHandle — це хук, який дає змогу налаштовувати дескриптор (handle) публічно доступного рефа.
useImperativeHandle(ref, createHandle, dependencies?)Викличте useImperativeHandle на верхньому рівні вашого компонента, щоб налаштувати дескриптор рефа, доступного з нього:
import { useImperativeHandle } from 'react';
function MyInput({ ref }) {
useImperativeHandle(ref, () => {
return {
// ... ваші методи ...
};
}, []);
// ...Перегляньте більше прикладів нижче.
-
ref:ref, який ви отримали як проп у компонентіMyInput. -
createHandle: Функція, яка не приймає аргументів і повертає дескриптор рефа, до якого ви хочете надати доступ. Дескриптор може бути будь-якого типу. Зазвичай, ви повертатимете об'єкт із методами, до яких ви хочете надати доступ. -
Опційний параметр
dependencies: Список усіх реактивних значень, на які посилається кодcreateHandle. Реактивні значення охоплюють пропси, стан і всі змінні та функції, оголошені безпосередньо в тілі компонента. Якщо ваш лінтер налаштований для React, він перевірить, чи кожне реактивне значення вказане як залежність. Список залежностей повинен містити стале число елементів, записаних у рядок як[залежність1, залежність2, залежність3]. React порівняє кожну залежність із своїм попереднім значенням, використовуючи функціюObject.is. Якщо повторний рендер призвів до зміни однієї із залежностей або якщо ви пропустили даний аргумент, ваша функціяcreateHandleвиконуватиметься повторно, і новостворений дескриптор буде призначений рефу.
Починаючи з React 19, ref доступний як проп. У React 18 і старіших версіях необхідно було отримувати ref із forwardRef.
useImperativeHandle повертає undefined.
Доступ батьківського елемента до налаштованого дескриптора рефа {/exposing-a-custom-ref-handle-to-the-parent-component/}
Щоб надати доступ до DOM-вузла батьківському елементу, передайте проп ref далі до цього вузла.
function MyInput({ ref }) {
return <input ref={ref} />;
};У коді вище реф, переданий компоненту MyInput, отримає DOM-вузол <input>. Однак, замість цього ви можете задати власне значення. Щоб налаштувати публічний дескриптор, викличте useImperativeHandle на верхньому рівні вашого компонента:
import { useImperativeHandle } from 'react';
function MyInput({ ref }) {
useImperativeHandle(ref, () => {
return {
// ... ваші методи ...
};
}, []);
return <input />;
};Зверніть увагу, що в наведеному вище коді ref більше не передається елементу <input>.
Наприклад, припустимо, що ви не хочете робити публічним весь DOM-вузол <input>, а лише два його методи: focus і scrollIntoView. У цьому разі зберігайте справжній браузерний DOM в окремому рефі. Потім викличте useImperativeHandle, щоб надати доступ до дескриптора, який містить лише методи, необхідні для виклику батьківським компонентом:
import { useRef, useImperativeHandle } from 'react';
function MyInput({ ref }) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input ref={inputRef} />;
};Тепер якщо батьківський компонент передасть реф до MyInput, він зможе викликати його методи focus і scrollIntoView. Однак, він не буде мати повного доступу до справжнього DOM-вузла <input>.
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 placeholder="Введіть ваше ім'я" ref={ref} />
<button type="button" onClick={handleClick}>
Редагувати
</button>
</form>
);
}import { useRef, useImperativeHandle } from 'react';
function MyInput({ ref, ...props }) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);
return <input {...props} ref={inputRef} />;
};
export default MyInput;input {
margin: 5px;
}Методи, які ви передаєте через імперативний дескриптор, не обов'язково мають точно збігатися з DOM методами. Наприклад, цей компонент Post передає метод scrollAndFocusAddComment через дескриптор. Це дає батьківському компоненту Page змогу прогорнути список коментарів і фокусувати поле введення, коли ви натискаєте кнопку:
import { useRef } from 'react';
import Post from './Post.js';
export default function Page() {
const postRef = useRef(null);
function handleClick() {
postRef.current.scrollAndFocusAddComment();
}
return (
<>
<button onClick={handleClick}>
Написати коментар
</button>
<Post ref={postRef} />
</>
);
}import { useRef, useImperativeHandle } from 'react';
import CommentList from './CommentList.js';
import AddComment from './AddComment.js';
function Post({ ref }) {
const commentsRef = useRef(null);
const addCommentRef = useRef(null);
useImperativeHandle(ref, () => {
return {
scrollAndFocusAddComment() {
commentsRef.current.scrollToBottom();
addCommentRef.current.focus();
}
};
}, []);
return (
<>
<article>
<p>Вітаю в моєму блозі!</p>
</article>
<CommentList ref={commentsRef} />
<AddComment ref={addCommentRef} />
</>
);
};
export default Post;import { useRef, useImperativeHandle } from 'react';
function CommentList({ ref }) {
const divRef = useRef(null);
useImperativeHandle(ref, () => {
return {
scrollToBottom() {
const node = divRef.current;
node.scrollTop = node.scrollHeight;
}
};
}, []);
let comments = [];
for (let i = 0; i < 50; i++) {
comments.push(<p key={i}>Коментар #{i}</p>);
}
return (
<div className="CommentList" ref={divRef}>
{comments}
</div>
);
}
export default CommentList;import { useRef, useImperativeHandle } from 'react';
function AddComment({ ref }) {
return <input placeholder="Додати коментар..." ref={ref} />;
}
export default AddComment;.CommentList {
height: 100px;
overflow: scroll;
border: 1px solid black;
margin-top: 20px;
margin-bottom: 20px;
}Не зловживайте рефами. Використовуйте рефи лише для імперативної поведінки, яку ви не можете виразити через пропси: наприклад, прогортування до вузла DOM, фокусування вузла, виклик анімації, виділення тексту тощо.
Якщо ви можете виразити щось як проп, тоді не варто використовувати реф. Наприклад, замість передавання імперативного дескриптора як { open, close } із компонента Modal, краще використати проп isOpen як <Modal isOpen={isOpen} />. Ефекти можуть допомогти вам надати доступ до імперативних частин через пропси.