Skip to content

Latest commit

 

History

History
292 lines (214 loc) · 10.3 KB

useImperativeHandle.md

File metadata and controls

292 lines (214 loc) · 10.3 KB
title
useImperativeHandle

useImperativeHandle це хук, який дозволяє кастомізувати об'єкт, який повертається через реф.

useImperativeHandle(ref, createHandle, dependencies?)

Опис {/reference/}

useImperativeHandle(ref, createHandle, dependencies?) {/useimperativehandle/}

Викличте useImperativeHandle на верхньому рівні вашого компонента, щоб кастомізувати об'єкт посилання, який повертає реф:

import { useImperativeHandle } from 'react';

function MyInput({ ref }) {
  useImperativeHandle(ref, () => {
    return {
      // ... ваші методи ...
    };
  }, []);
  // ...

Перегляньте більше прикладів нижче.

Параметри {/parameters/}

  • ref: Реф, який ви отримали як проп у компоненті MyInput.

  • createHandle: Функція, яка не приймає аргументів і повертає об'єкт посилання, який ви хочете надати. Цей об’єкт може бути будь-якого типу. Зазвичай, ви повертатимете об'єкт з методами, які ви захочете використовувати.

  • Опціональний параметр dependencies: Список усіх реактивних значень, на які посилається код createHandle. Реактивні значення включають в себе пропси, стан та всі змінні та функції, оголошені безпосередньо в тілі компонента. Якщо ваш лінтер налаштований для Реакту, він перевірить, чи кожне реактивне значення вказане як залежність. Список залежностей повинен містити стале число елементів, записаних в рядок як [залежність1, залежність2, залежність3]. Реакт порівняє кожну залежність із своїм попереднім значенням використовуючи порівняння Object.is. Якщо повторний рендер призвів до зміни однієї із залежностей або якщо ви пропустили даний аргумент, ваша createHandle функція буде виконана повторно і новостворений об'єкт посилання буде призначений до рефу.

Починаючи з React 19 реф доступний як проп. У React 18 і старіших версіях необхідно було отримувати реф з forwardRef.

Результат {/returns/}

useImperativeHandle повертає undefined.


Використання {/usage/}

Надання кастомного об'єкта посилання батьківському компоненту {/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;
}

Передавання власних імперативних методів {/exposing-your-own-imperative-methods/}

Методи, які ви передаєте через імперативний обробник не обов'язково мають точно відповідати 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 може допомогти вам задавати імперативну поведінку через пропси.