react forwardrefでコンポーネントの参照を柔軟に扱う方法

はじめに

Reactを学び始めると、コンポーネント同士のデータの受け渡しや、UI要素への参照の管理が大切だと感じる場面が多いのではないでしょうか。 しかし、単に props を渡すだけでは手が届かないケースもあります。 たとえば、親コンポーネントから子コンポーネントへ ref を渡して、子の内部にある要素を直接操作したいことがあると思います。 こうしたときに役立つのが react forwardRef の仕組みです。 この記事では、初心者の方にもわかりやすい言葉で forwardRef の使い方や具体的な活用シーンを紹介していきます。

この記事を読むとわかること

皆さんがこの記事を読み終わると、以下のようなことが理解できるでしょう。 React で ref を扱う基本的な考え方や、forwardRef を使用する具体的なメリット。 さらに、UI要素を制御するためのコード例や、実務で見られる活用シーンも紹介します。 どのような場面で forwardRef が有効なのか、その判断材料も得られるはずです。

react forwardRefとは?

react forwardRef は、親コンポーネントから子コンポーネントへ ref を渡すための仕組みとして用意されています。 通常の props では参照を受け渡すことができませんが、forwardRef を使うと子コンポーネント内部の要素への直接アクセスや操作が可能になります。 これにより、親側で子の状態管理や DOM 操作を柔軟に行うことができるため、UIの細かい制御が行いやすくなるのです。

ref 自体は、DOM 要素やコンポーネントのインスタンスに対して、直接アクセスを可能にするものです。 React でコンポーネントの再レンダリング制御やフォーカス管理を行いたいときに役立ちますが、通常は子コンポーネント側で ref を受け取るときに工夫が必要です。 forwardRef を利用することで、この「工夫」部分を簡素化できるのが大きな特徴です。

もし皆さんが React で、ボタンをクリックしたら特定の入力フォームにフォーカスを当てたいとか、動画プレイヤーを制御したいなどの用途を考えたとき、forwardRef はかなり便利な手段になるでしょう。 ただし、すべてのケースで利用すべきというわけではなく、対象となる子コンポーネントの実装内容や、props を経由したやり取りで十分かどうかを見極めることも大事です。

refの基本的な役割

ref とは「参照」という意味で、React では要素やコンポーネントに直接アクセスするために使われます。 たとえば、フォーム入力などでフォーカス制御が必要な場面や、アニメーションで DOM 要素を直接操作する場面で活躍します。 通常は useRef フックを利用して定義し、DOM 要素へ関連付けることで、その要素を操作できるようになります。

いくつかのケースでは単に親コンポーネント内だけで事が足りるため、forwardRef を使うほどでもないということもあるでしょう。 しかし、子コンポーネントにある input 要素や canvas など、親コンポーネントから直接触りたいときは少なくありません。 そういったときに、forwardRef を利用するとシンプルに解決できます。

ref の使い方を間違えると、意図しないタイミングでコンポーネントを操作してしまうことがあります。 React では、なるべくステートや props を通じたデータの受け渡しを推奨していますが、UI要素への最終的なアクセスが必要な場合に ref が役立つ、というイメージを持っておくとわかりやすいです。

forwardRefの基本的な使い方

react forwardRef を使うには、以下のように関数コンポーネントを forwardRef でラップします。 すると、コンポーネントの第二引数として ref が受け取れるようになります。 そこから先は、子コンポーネントの内部にある要素やメソッドに対して参照を渡すことが可能になります。

import React, { forwardRef } from "react";

const ChildComponent = forwardRef((props, ref) => {
  return (
    <div>
      <input type="text" ref={ref} />
    </div>
  );
});

export default ChildComponent;

この例では、ChildComponent 内の input に対して ref を割り当てています。 実際に使う場面では、親コンポーネント側で useRef を定義し、その ref を子コンポーネントへ渡すという流れになります。 コードのイメージは以下のようになります。

import React, { useRef } from "react";
import ChildComponent from "./ChildComponent";

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

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

  return (
    <div>
      <ChildComponent ref={inputRef} />
      <button onClick={focusOnInput}>フォーカスを当てる</button>
    </div>
  );
}

export default ParentComponent;

この仕組みを使えば、親コンポーネントから子コンポーネントの内部要素へフォーカスを与えたり、値をクリアしたりといった操作が可能になります。 実務でもフォームのエラーチェックや、特定イベント発火時の UI 変更などで便利に使えます。

forwardRefとuseImperativeHandleの組み合わせ

forwardRef とあわせて useImperativeHandle フックを使うと、子コンポーネントのインスタンスに対して必要最小限のインターフェイスを公開できます。 これは、子コンポーネントが持つあらゆる情報をすべて丸ごと公開するのではなく、必要なメソッドやプロパティだけを親に開放できるのがポイントです。

たとえば下記のように、子コンポーネント側で useImperativeHandle を使って公開したいメソッドを定義すると、親コンポーネントからそれを呼び出すことができます。

import React, { useRef, forwardRef, useImperativeHandle } from "react";

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

  useImperativeHandle(ref, () => ({
    focusInput: () => {
      if (inputRef.current) {
        inputRef.current.focus();
      }
    },
    clearInput: () => {
      if (inputRef.current) {
        inputRef.current.value = "";
      }
    }
  }));

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

export default function ParentComponent() {
  const customInputRef = useRef(null);

  const handleFocus = () => {
    if (customInputRef.current) {
      customInputRef.current.focusInput();
    }
  };

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

  return (
    <div>
      <CustomInput ref={customInputRef} />
      <button onClick={handleFocus}>フォーカスを当てる</button>
      <button onClick={handleClear}>クリアする</button>
    </div>
  );
}

このように、親コンポーネントから focusInputclearInput といったメソッドを呼び出せるようになります。 公開範囲を絞ることで、コンポーネントの設計をわかりやすく保ちつつ、必要な操作だけを可能にするというメリットが得られます。 内部の実装を完全に隠蔽するわけではありませんが、最低限の操作だけを開放することでメンテナンスのしやすさにつながります。

forwardRefの活用シーン

forwardRef は、どのような場面で使うと便利なのでしょうか。 主なシーンとしては、以下のような用途が考えられます。

  • 子コンポーネント内部の特定要素へフォーカスやスクロールを行いたいとき
  • 親コンポーネントから子コンポーネントのメソッドを呼び出したいとき
  • UI ライブラリなどで、ラップしたコンポーネントの DOM にアクセス可能にしたいとき

たとえば、モーダルウィンドウの中にある要素を制御したい場面や、外部ライブラリと連携するときに DOM 要素を取得する必要があるケースで活躍します。 また、自作の入力部品やテキストエディタなどをコンポーネント化するときにも重宝します。 単純に親子間で props の受け渡しを行うだけでは実現しにくい操作をシンプルに行えるので、コンポーネント間の責務がはっきりしているほど使いやすいです。

過度に forwardRef を多用すると、コンポーネント間の依存関係が複雑になる可能性があります。 本当に必要なときだけ使うのがよいでしょう。

さらに、UIライブラリの設計を行う際は、ユーザーが直接 DOM にアクセスできるようにするためのエントリーポイントとして forwardRef が用いられることがあります。 これにより、ライブラリ利用者はラップされたコンポーネントの内部要素に対して、フォーカスや位置制御などを自由に行えます。

実務での注意点

forwardRef は強力ですが、いくつか気をつけたいポイントもあります。 まず、ref を使うということは DOM に直接アクセスすることを意味し、React の「状態管理」や「仮想DOM での再レンダリング制御」とは別の考え方が必要です。 できるだけ props やステートで済むならそちらを優先したほうが、コンポーネント間の責務がわかりやすくなります。

また、forwardRef を使い始めると、親コンポーネントが子コンポーネントの内部実装に強く依存するケースが増えるかもしれません。 このような構造は、後のリファクタリングで影響が広範囲に及ぶ可能性があるので、明確な目的がある場合のみに限定しましょう。

コンポーネントを再利用する場合、内部で forwardRef を使っているかどうかを理解しておかないと、意図しない動作を引き起こすことがあります。

さらに、state や effect との組み合わせを考える場合、子コンポーネントをなぜ直接操作したいのかを再確認することが大切です。 ユーザーアクションやイベントに応じた動的な UI の変化を行うなら、props やコンテキストでデータをやり取りする方法も選択肢になり得ます。

まとめ

この記事では react forwardRef の概要と基本的な使い方、そして実務での活用シーンについて解説しました。 ref 自体は初心者にとって馴染みが薄いかもしれませんが、DOM操作やフォーカス管理が必要な場面では役立ちます。 forwardRef を利用すると、親コンポーネントから子コンポーネント内部への参照が簡潔に書けるので、UI 要素のコントロールがしやすくなります。

ただし、React では基本的に props やステートを通してデータや状態を管理するのが一般的です。 そのため、forwardRef を頻繁に使いすぎると、コンポーネント同士の依存関係が複雑化してしまう可能性もあります。 適材適所で useRef や forwardRef を使い分けることで、コードの保守性や可読性を保ちながら DOM やメソッドへのダイレクトな操作を行うようにしましょう。

皆さんの開発現場で、「子コンポーネントをもっと柔軟に操作したい」と思ったとき、この記事の知識が少しでもお役に立てば嬉しいです。

Reactをマスターしよう

この記事で学んだReactの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。