useImperativeHandleの使い方

今日は、`useImperativeHandle`を使いつつ学んでいきます。
[公式:useImperativeHandle](https://ja.react.dev/reference/react/useImperativeHandle)

## 前提

forwardRefについて知っておく必要がりますので、まずはこちらを読んでおいてくださいね！
[公式:forwardRef](https://ja.react.dev/reference/react/forwardRef)
  - ざっくりいうと...。親は子にrefを渡す、子はforwardRefしておくことで、子の要素を親で触れるようにできるよというものデス。

## useImperativeHandleって何？
親コンポーネントが子コンポーネントの内部メソッドを呼び出せるようにするフックです。！
これにより、コンポーネントの内部実装を隠しつつ、親に特定の機能だけを公開できます。
親は必要な操作だけを行え、子の詳細が変更されても影響を受けません！

## 基本の使い方

```jsx
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// 子コンポーネント
const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef(null);

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

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

// 親コンポーネント
const Form = () => {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.setFocus();
  };

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={handleClick}>inputにフォーカスする</button>
    </div>
  );
}
```

### 親コンポーネントからrefを渡す

```jsx
<CustomInput ref={inputRef} />
```

親側で定義した `const inputRef = useRef(null);` を、CustomInputに渡します。

### 子コンポーネントでuseImperativeHandleを使用

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

ここでrefは親から渡されたものであり、そのrefに対してsetFocusメソッドを定義しています。
このsetFocusメソッドは、親から呼び出すことができます。


## もうちょっとメソッドを増やしてみます

```jsx
import React, { useRef, useImperativeHandle, forwardRef } from 'react';

// 子コンポーネント
const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(ref, () => ({
    setFocus: () => {
      inputRef.current.focus();
    },
    clearInput: () => {
      inputRef.current.value = '';
    },
    highlightText: () => {
      inputRef.current.select();
    }
  }));

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

// 親コンポーネント
const Form = () => {
  const inputRef = useRef(null);

  const handleFocus = () => {
    inputRef.current.setFocus();
  };

  const handleClear = () => {
    inputRef.current.clearInput();
  };

  const handleSelectText = () => {
    inputRef.current.highlightText();
  };

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={handleFocus}>フォーカス</button>
      <button onClick={handleClear}>クリア</button>
      <button onClick={handleSelectText}>テキスト選択</button>
    </div>
  );
}
```

子が`useImperativeHandle`して公開したメソッドを、refを通じて親が利用していることがわかりますね！

## 依存配列

`useImperativeHandle`は`useEffect`のように依存配列を持てます。

```jsx
  const [value, setValue] = useState('');
 
  useImperativeHandle(ref, () => ({
    setFocus: () => {
      inputRef.current.focus();
    },
    clearInput: () => {
      inputRef.current.value = '';
      setValue('');
    },
    highlightText: () => {
      inputRef.current.select();
    }
  }), [value]); // 依存配列として`value`を指定
```

### useImperativeHandleに依存配列を入れたとき

- 依存関係が変化したときのみハンドルを再生成し、パフォーマンスを最適化

### useImperativeHandleに依存配列を入れなかったとき
- コンポーネントが再レンダリングされる度に`useImperativeHandle`の関数が再実行され、公開されるインペラティブハンドルが毎回再生成されます。
  - `インペラティブハンドル?` : 親が子の内部メソッドやプロパティにアクセスできる仕組み。 
- パフォーマンスに悪影響を与える可能性があります！


## useImperativeHandleを使わなくてもよい

```jsx
import React, { useRef } from 'react';

function useCustomInput() {
  const inputRef = useRef(null);

  const setFocus = () => {
    inputRef.current.focus();
  };

  const clearInput = () => {
    inputRef.current.value = '';
  };

  return {
    inputRef,
    setFocus,
    clearInput,
  };
}

// 子コンポーネント
const CustomInput = ({ inputRef, ...props }) => (
  <input ref={inputRef} {...props} />
);

// 親コンポーネント
const Form = () => {
  const { inputRef, setFocus, clearInput } = useCustomInput();

  return (
    <div>
      <CustomInput inputRef={inputRef} />
      <button onClick={setFocus}>フォーカス</button>
      <button onClick={clearInput}>クリア</button>
    </div>
  );
}
```


## まとめ
今日は`useImperativeHandle`の使い方について学びました！
このフックを使うことで、親が子の内部メソッドを呼び出せるようになり、再利用性や柔軟性が向上しますね！
場合によっては、`useImperativeHandle`を使わなくても、カスタムフックやヘルパー関数を使って同様の機能を実現できます。

これらの方法を使い分けて、Reactコンポーネントの設計をより柔軟かつ効率的に行いましょう。