【Reactフック】useImperativeHandleによるカスタムハンドルの作成

JavaScript

Reactフックの一つであるuseImperativeHandleは、親コンポーネントが子コンポーネントの内部インスタンスを制御できるようにするために使用されます。

このフックは、特にカスタムハンドルを作成し、親コンポーネントに特定のメソッドやプロパティを公開したい場合に役立ちます。

基本的な使い方

useImperativeHandleは、forwardRefuseRefと組み合わせて使用されます。逆に言えば、useImperativeHandleを使用するにあたりforwardRefuseRefを使わないケースはほぼないと言えるでしょう。

useImperativeHandleはカスタムフックを作成するためのフックであり、ref オブジェクトに対してカスタムのインスタンス値を公開します。
一方、forwardRefは子コンポーネントに対してrefを渡すために使用されます。
そしてuseRefrefオブジェクトを生成するために使用されます。
つまり、useImperativeHandlerefのカスタマイズを行うためには、最初にuseRefrefを生成し、そのrefforwardRefで子コンポーネントに渡し、そこでuseImperativeHandleを使うという流れになります。

基本的な使い方の例を見てみましょう。

サンプルプログラム
import React, { useImperativeHandle, useRef, forwardRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));

  return <input ref={inputRef} />;
});

function Parent() {
  const inputRef = useRef();

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
}

よくある使い方と実践的なサンプル

1. 複数のメソッドを公開する場合

サンプルプログラム
import React, { useImperativeHandle, useRef, forwardRef } from 'react';

const CustomComponent = forwardRef((props, ref) => {
  const localRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      localRef.current.focus();
    },
    reset: () => {
      localRef.current.value = '';
    }
  }));

  return <input ref={localRef} />;
});

function ParentComponent() {
  const customRef = useRef();

  return (
    <div>
      <CustomComponent ref={customRef} />
      <button onClick={() => customRef.current.focus()}>Focus</button>
      <button onClick={() => customRef.current.reset()}>Reset</button>
    </div>
  );
}

2. 子コンポーネントの状態を制御する場合

サンプルプログラム
import React, { useImperativeHandle, useRef, forwardRef, useState } from 'react';

const ToggleComponent = forwardRef((props, ref) => {
  const [isVisible, setIsVisible] = useState(true);

  useImperativeHandle(ref, () => ({
    toggleVisibility: () => {
      setIsVisible(prevState => !prevState);
    }
  }));

  return (
    <div>
      {isVisible && <div>Toggle me!</div>}
    </div>
  );
});

function ParentComponent() {
  const toggleRef = useRef();

  return (
    <div>
      <ToggleComponent ref={toggleRef} />
      <button onClick={() => toggleRef.current.toggleVisibility()}>Toggle Visibility</button>
    </div>
  );
}

ベストプラクティスと注意点

ベストプラクティス

  • useImperativeHandleは、他の解決策がない場合のみに使用することを検討しましょう。Reactのデザインパターンに反する可能性があるためです。
  • 必要最小限のメソッドやプロパティのみを公開し、コンポーネントのインターフェースをシンプルに保ちましょう。

注意点

  • useImperativeHandleを使用する際には、forwardRefと組み合わせて使うことを忘れないようにしましょう。
  • 過度な使用は避け、他のReactフックやコンポーネント構造の再検討を優先しましょう。
    • useImperativeHandleを多用すると、コンポーネントの再利用性が低下し、特定の親コンポーネントに依存する設計になりがちです。また、テストが難しくなることや、他のフックとの組み合わせが難しくなることもあります。さらに、予期しない副作用が発生するリスクもあるため、慎重に使用することが重要です。

まとめ

useImperativeHandleは、親コンポーネントが子コンポーネントの内部インスタンスを制御できるようになるため、強力なツールと言えます。

ただし、ベストプラクティスや注意点に記載した理由により、他に解決策がない場合のみに使用することをおすすめします。

本記事についての質問、誤りの指摘、ご意見ご感想などありましたら、ぜひコメント頂ければ幸いです。 それでは、最後までお読みいただき、ありがとうございます。

コメント

タイトルとURLをコピーしました