【React】useRef/forwardRefの概要と使いどころ

JavaScript

Refについて

Refとはrenderメソッドで作成されたDOMノードもしくはReact要素の参照を保持するオブジェクトです。
Refオブジェクトのcurrent属性を参照することでノードにアクセスできます。

Refの使いどころ

Refを利用するユースケースは以下のとおりです。1

  • フォーカス、テキストの選択およびメディアの再生の管理
  • アニメーションの発火
  • サードパーティのDOMライブラリとの統合

注意点として、React『Ref を使いすぎない』にも記述されているとおり、Refの多用は控えるようにしましょう。
『データフローはPropsを介して行う』『状態はStateに保存』がReactの原則です。

useRefについて

useRefとはRefオブジェクトを生成するフックです。
useRef(initialValue)で初期値がinitialValueのミュータブルなRefオブジェクトを生成します。

なお、ミュータブル(mutable)なデータとは『一度作成した後も値が変更できる』性質を持つデータのことをいいます。

useRefの利用手順

  1. useRefでRefオブジェクトを生成する
  2. 生成したRefオブジェクトを、参照したいDOMのref属性にセットする
  3. Refオブジェクトのcurrent属性を参照してノードにアクセスする

useRefの具体例

入力フォームにフォーカスする

import { FC, useRef } from "react";

const App: FC = () => {
  const inputEl = useRef<HTMLInputElement>(null);
  const handleClick = () => {
    inputEl.current?.focus();

    console.log("inputEl.current: ", inputEl.current);
    // inputEl.current: <input type="text">
  };
  return (
    <>
      <input type="text" ref={inputEl} />
      <button onClick={handleClick}>Focus the input</button>
    </>
  );
};

export default App;

DOMの高さを取得する

App.tsx

import { FC } from "react";
import { Layout } from "./Layout";

const App: FC = () => {
  return (
    <Layout>
      <div style={{ height: "300px" }}>Example</div>
    </Layout>
  );
};

export default App;

Layout.tsx

import { FC, useEffect, useRef } from "react";

export const Layout: FC = ({ children }) => {
  const childElement = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const height = childElement.current?.clientHeight;
    console.log("height: ", height);
    // => height:  300
  }, []);

  return <div ref={childElement}>{children}</div>;
};

forwardRefについて

親コンポーネントから子コンポーネントのDOMにアクセスしたい、つまり親コンポーネントから子コンポーネントのRefを参照したいケースがあります。
親コンポーネントで子コンポーネントのRefを参照するには子コンポーネントにref属性を追加します。

しかし子コンポーネントが関数コンポーネントの場合、関数コンポーネントにはインスタンスがないためref属性を使用できません。1

forwardRefは関数コンポーネントでもref属性を使用できるようにするための機能です。

なお、コンポーネントをまたいでrefを自動的に渡すテクニックのことをrefのフォワーディングと呼びます。
つまり、forwardRefは関数コンポーネントでrefのフォワーディングを実現するためのものといえます。

forwardRefの利用手順

  1. 関数コンポーネントをforwardRef()で囲む
  2. 第一引数にProps、第二引数にRefを指定する
  3. 参照したいDOMのref属性にRefをセットする

Refを参照する場合は、useRefで生成したRefオブジェクトを当該コンポーネントのref属性に渡します。
なお、forwardRefの型定義はforwardRef<Refの型, Propsの型>となります。

forwardRefの具体例

forwardRefの具体例として、入力フォームにフォーカスする実装を紹介します。

App.tsx

import { FC, useRef } from "react";
import { CustomInput } from "./CustomInput";

const App: FC = () => {
  const inputEl = useRef<HTMLInputElement>(null);
  const handleClick = () => {
    inputEl.current?.focus();

    console.log("inputEl.current: ", inputEl.current);
    // inputEl.current: <input type="text" placeholder="this is example" />;
  };
  return (
    <>
      <CustomInput ref={inputEl} placeholder={"this is example"} />
      <button onClick={handleClick}>Focus the input</button>
    </>
  );
};

export default App;

CustomInput.tsx

import { forwardRef } from "react";

type Props = {
  placeholder: string;
};

export const CustomInput = forwardRef<HTMLInputElement, Props>(
  ({ placeholder }, ref) => {
    return <input type="text" ref={ref} placeholder={placeholder} />;
  }
);

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料