title |
---|
useImperativeHandle |
useImperativeHandle
це хук, який дозволяє кастомізувати об'єкт, який повертається через реф.
useImperativeHandle(ref, createHandle, dependencies?)
Викличте useImperativeHandle
на верхньому рівні вашого компонента, щоб кастомізувати об'єкт посилання, який повертає реф:
import { useImperativeHandle } from 'react';
function MyInput({ ref }) {
useImperativeHandle(ref, () => {
return {
// ... ваші методи ...
};
}, []);
// ...
Перегляньте більше прикладів нижче.
-
ref
:Реф
, який ви отримали як проп у компонентіMyInput
. -
createHandle
: Функція, яка не приймає аргументів і повертає об'єкт посилання, який ви хочете надати. Цей об’єкт може бути будь-якого типу. Зазвичай, ви повертатимете об'єкт з методами, які ви захочете використовувати. -
Опціональний параметр
dependencies
: Список усіх реактивних значень, на які посилається кодcreateHandle
. Реактивні значення включають в себе пропси, стан та всі змінні та функції, оголошені безпосередньо в тілі компонента. Якщо ваш лінтер налаштований для Реакту, він перевірить, чи кожне реактивне значення вказане як залежність. Список залежностей повинен містити стале число елементів, записаних в рядок як[залежність1, залежність2, залежність3]
. Реакт порівняє кожну залежність із своїм попереднім значенням використовуючи порівнянняObject.is
. Якщо повторний рендер призвів до зміни однієї із залежностей або якщо ви пропустили даний аргумент, вашаcreateHandle
функція буде виконана повторно і новостворений об'єкт посилання буде призначений до рефу.
Починаючи з React 19 реф
доступний як проп. У React 18 і старіших версіях необхідно було отримувати реф
з 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 />;
};
Зверніть увагу, що в наданому вище коді реф
більше не передається до <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="Enter your name" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</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}>
Write a comment
</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>Welcome to my blog!</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}>Comment #{i}</p>);
}
return (
<div className="CommentList" ref={divRef}>
{comments}
</div>
);
}
export default CommentList;
import { useRef, useImperativeHandle } from 'react';
function AddComment({ ref }) {
return <input placeholder="Add comment..." 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} />
. Effects може допомогти вам задавати імперативну поведінку через пропси.