React: 子コンポーネント内部の関数を、親コンポーネントから操作する(useImperativeHandle)

子コンポーネント内部の状態を変更する処理、例えば、FormikなどContext Apiを使用している処理を、その外側のコンポーネントから操作したい場合

親コンポーネントの関数を、props経由で子コンポーネントに渡すことは容易ですが、その逆はどのようにすればよいでしょうか?

解決方法

useImperativeHandle というhookを使用すると、子コンポーネントを操作できる参照を親コンポーネントに渡すことができます。

実装

ものすごく簡単ですが、useStateの変数を表示するだけのコンポーネントを作成し、

その変数を操作する関数を、親コンポーネントに公開してみます

操作対象のコンポーネントを実装する

import {
  useState,
  ForwardRefRenderFunction,
  forwardRef,
  useImperativeHandle,
} from 'react'

export type ChildRefType = {
  increment: () => void
  clear: () => void
}

const Child: ForwardRefRenderFunction<ChildRefType> = (_, ref) => {
  const [count, setCount] = useState(0);

  useImperativeHandle(ref, () => ({
    increment: () => setCount(v => v + 1),
    clear: () => setCount(0)
  }))

    return (
    <div>{count}</div>
  )
}

export default forwardRef(Child)

この部分が、親コンポーネントに公開する操作の設定です。

incrementでカウントを進めて、clearでカウントをクリアします。

useImperativeHandle(ref, () => ({
  increment: () => setCount(v => v + 1),
  clear: () => setCount(0)
}))

forwardRefを使って、exportします。

export default forwardRef(Child)

親コンポーネントから子コンポーネントを操作する

作成した子コンポーネント(Child)を操作する親コンポーネントの実装です。

import { FC, useRef } from 'react'
import Child, { ChildRefType } from './Child'

const Parent: FC = () => {
  const childRef = useRef<ChildRefType>(null);
  return (
    <div>
      <Child ref={childRef} />
      <button onClick={() => childRef.current?.increment()}>increment</button>
      <button onClick={() => childRef.current?.clear()}>clear</button>
    </div>
  );
}

export default Parent

これで、画面に表示されるincrementボタン、clearボタンを押下すると、
親コンポーネントから、子コンポーネントの内部状態であるuseStateを、公開した関数を通して操作が可能になっています