useImperativeHandle は、refとして公開されるハンドルをカスタマイズできる React フックです。

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 のレンダー関数から2番目の引数として受け取った ref

  • createHandle: 引数を取らず、公開する ref ハンドルを返す関数。その ref ハンドルは任意の型を持つことができます。通常、公開したいメソッドを持つオブジェクトを返します。

  • 任意 dependencies: createHandle コード内で参照されるすべてのリアクティブ値のリスト。リアクティブ値には、props、state、およびコンポーネント本体内で直接宣言されたすべての変数と関数が含まれます。リンターがReact用に構成されている場合、すべてのリアクティブ値が依存関係として正しく指定されていることを確認します。依存関係のリストは、アイテム数が一定で、[dep1, dep2, dep3]のようにインラインで記述する必要があります。Reactは、Object.is比較を使用して、各依存関係を以前の値と比較します。再レンダリングによって一部の依存関係に変更があった場合、またはこの引数を省略した場合、createHandle関数が再実行され、新しく作成されたハンドルがrefに割り当てられます。

戻り値

useImperativeHandleundefined を返します。


使い方

親コンポーネントへのカスタム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ノード全体を公開したくないが、そのメソッドのうち2つ、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を取得した場合、そのrefに対してfocusメソッドとscrollIntoViewメソッドを呼び出すことができるようになります。ただし、基盤となる<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} />
    </>
  );
}

落とし穴

refsを過度に使用しないでください。 refsは、propsとして表現できない命令型の動作(例えば、ノードへのスクロール、ノードへのフォーカス、アニメーションのトリガー、テキストの選択など)にのみ使用する必要があります。

何かをpropsとして表現できる場合は、refを使用するべきではありません。例えば、Modalコンポーネントから{ open, close }のような命令型ハンドルを公開する代わりに、<Modal isOpen={isOpen} />のようにisOpenをpropsとして受け取る方が良いでしょう。Effectsは、propsを介して命令型の動作を公開するのに役立ちます。