useImperativeHandle 完全入門【React】refで公開するAPIを制御する

ReactのuseImperativeHandleとは何か、親コンポーネントから子コンポーネントのメソッドを呼び出す方法を初心者向けに解説。forwardRefとの組み合わせ方や具体的な使用例も紹介します。

#react#hooks#useimperativehandle#javascript#frontend

「子コンポーネントのフォームにフォーカスを当てたい」「子コンポーネントのアニメーションを親から制御したい」——そんなときに使うのが useImperativeHandle です。

通常のReactでは、データはpropsを通じて親から子へ流れます。しかし、DOMの操作やアニメーションなど命令的な操作が必要な場面では、親から子のメソッドを直接呼び出したいことがあります。useImperativeHandle はこのような場面で役立つフックです。

この記事でわかること:

  • useImperativeHandle が必要な場面
  • forwardRef との組み合わせ方
  • 親から子のメソッドを呼び出す実装パターン
  • 使いすぎを避けるためのベストプラクティス

useImperativeHandle とは?

useImperativeHandle は、親コンポーネントに公開する ref の内容をカスタマイズするフックです。

通常 ref をコンポーネントに渡すと、親はそのコンポーネントの DOM ノード全体にアクセスできてしまいます。useImperativeHandle を使うと、公開するメソッドを必要なものだけに限定できます。

useImperativeHandle(ref, createHandle関数, 依存配列);
パラメータ説明
ref親から渡された ref オブジェクト
createHandle公開するメソッドを含むオブジェクトを返す関数
dependencies(省略可能)再生成のトリガーとなる値の配列

戻り値は undefined です。

基本的な使い方

React 19 以降では ref が通常の props として渡せるようになりました。

import { useRef, useImperativeHandle } from "react";
 
// 子コンポーネント
function FancyInput({ ref }) {
  const inputRef = useRef(null);
 
  // 親に公開するメソッドを定義
  useImperativeHandle(ref, () => ({
    focus() {
      inputRef.current.focus();
    },
    clear() {
      inputRef.current.value = "";
    },
  }));
 
  return <input ref={inputRef} type="text" />;
}
 
// 親コンポーネント
function Form() {
  const fancyInputRef = useRef(null);
 
  const handleClick = () => {
    fancyInputRef.current.focus(); // 子のfocusメソッドを呼び出す
  };
 
  return (
    <>
      <FancyInput ref={fancyInputRef} />
      <button onClick={handleClick}>フォーカス</button>
    </>
  );
}

React 18 以前の書き方(forwardRef)

React 18 以前では forwardRef でコンポーネントをラップする必要があります。

import { useRef, useImperativeHandle, forwardRef } from "react";
 
// forwardRef でラップする
const FancyInput = forwardRef(function FancyInput(props, ref) {
  const inputRef = useRef(null);
 
  useImperativeHandle(ref, () => ({
    focus() {
      inputRef.current.focus();
    },
    scrollIntoView() {
      inputRef.current.scrollIntoView({ behavior: "smooth" });
    },
  }));
 
  return <input ref={inputRef} type="text" />;
});

実用的な使用例:動画プレイヤーの制御

import { useRef, useImperativeHandle } from "react";
 
function VideoPlayer({ src, ref }) {
  const videoRef = useRef(null);
 
  // play/pause/seek だけを親に公開する
  useImperativeHandle(ref, () => ({
    play() {
      videoRef.current.play();
    },
    pause() {
      videoRef.current.pause();
    },
    seekTo(seconds) {
      videoRef.current.currentTime = seconds;
    },
  }));
 
  return <video ref={videoRef} src={src} />;
}
 
function App() {
  const playerRef = useRef(null);
 
  return (
    <>
      <VideoPlayer ref={playerRef} src="/movie.mp4" />
      <button onClick={() => playerRef.current.play()}>再生</button>
      <button onClick={() => playerRef.current.pause()}>一時停止</button>
      <button onClick={() => playerRef.current.seekTo(30)}>30秒へ</button>
    </>
  );
}

いつ使うべきか?避けるべきか?

useImperativeHandle命令的な操作にのみ使うのが原則です。

適切な使用例:

  • フォームへのフォーカス
  • スクロール位置の制御
  • アニメーションのトリガー
  • テキストの選択

避けるべきケース:

// ❌ NG:propsで表現できるものにrefを使わない
// Modal に open/close メソッドを持たせるより...
ref.current.open();
 
// ✅ OK:isOpen props で制御する
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} />

なるべく props でデータを渡して宣言的に書くことがReactの推奨スタイルです。useImperativeHandle は「どうしても命令的に操作しないといけない」場面の最終手段と考えてください。

まとめ

  • useImperativeHandle は親コンポーネントに公開する ref の内容をカスタマイズするフック
  • DOM ノード全体ではなく、必要なメソッドだけを公開できる
  • React 19 以降は forwardRef なしで使えるが、18 以前は forwardRef が必要
  • 命令的な操作(フォーカス・スクロール・アニメーション)にのみ使い、props で代替できる場面では使わない

関連記事