useImperativeHandle

useImperativeHandle 是一個 React Hook,可讓你自訂公開為 ref 的控制代碼。

useImperativeHandle(ref, createHandle, dependencies?)

參考

useImperativeHandle(ref, createHandle, dependencies?)

在元件的頂層呼叫 useImperativeHandle來自訂它公開的 ref 控制代碼

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... your methods ...
};
}, []);
// ...

請參閱下面的更多範例。

參數

  • ref:你從 forwardRef 渲染函式接收的第二個參數 ref。

  • createHandle:一個不接受任何參數並返回你想公開的 ref 控制代碼的函式。該 ref 控制代碼可以是任何類型。通常,你會返回一個包含你想公開方法的物件。

  • 選用 dependenciescreateHandle程式碼內參考的所有反應值的列表。反應值包括 props、狀態,以及直接在元件主體內宣告的所有變數和函式。如果你的程式碼檢查器已針對 React 進行設定,它將驗證每個反應值是否已正確指定為依賴項。依賴項列表必須具有固定數量的項目,並且必須像 [dep1, dep2, dep3]一樣內嵌撰寫。React 將使用 Object.is 比較將每個依賴項与其先前值進行比較。如果重新渲染導致某些依賴項發生更改,或者你省略了此參數,則你的 createHandle 函式將重新執行,並且新建立的控制代碼將被指派給 ref。

回傳值

useImperativeHandle 回傳 undefined


用法

將自定義 ref 控制代碼暴露給父組件

預設情況下,組件不會將其 DOM 節點暴露給父組件。例如,如果您希望 MyInput 的父組件能夠存取 <input> DOM 節點,您必須使用 forwardRef 來選擇加入:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
return <input {...props} ref={ref} />;
});

透過上面的程式碼,指向 MyInput 的 ref 將會收到 <input> DOM 節點。 但是,您可以改為暴露自定義值。要自定義暴露的控制代碼,請在組件的頂層呼叫 useImperativeHandle

import { forwardRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
useImperativeHandle(ref, () => {
return {
// ... your methods ...
};
}, []);

return <input {...props} />;
});

請注意,在上面的程式碼中,ref 不再轉發到 <input>

例如,假設您不想暴露整個 <input> DOM 節點,但您想暴露它的兩個方法:focusscrollIntoView。要做到這一點,請將真實的瀏覽器 DOM 保留在一個單獨的 ref 中。然後使用 useImperativeHandle 暴露一個只包含您希望父組件呼叫的方法的控制代碼

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 的 ref,它將能夠在其上呼叫 focusscrollIntoView 方法。但是,它將無法完全存取底層的 <input> 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>
  );
}


暴露您自己的指令式方法

您透過指令式控制代碼暴露的方法不必與 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} />
    </>
  );
}

陷阱

不要過度使用 ref。 您應該只將 ref 用於您無法以 props 表達的*指令式*行為:例如,滾動到節點、聚焦節點、觸發動畫、選擇文字等等。

如果您可以將某些內容表達為 prop,則不應使用 ref。 例如,與其從 Modal 組件暴露 { open, close } 之類的指令式控制代碼,不如將 isOpen 作為 prop,例如 <Modal isOpen={isOpen} />效果可以幫助您透過 props 暴露指令式行為。

(先前章節連結)useId(下一章節連結)useInsertionEffect