💼

React 19 forwardRefのサポート終了

に公開

forwardRefとは何ですか?

Reactにおいて、refはコンポーネントやDOM要素へ直接アクセスするための重要な機能です。

  • ボタンにフォーカスを当てたいとき
  • テキスト入力欄の値を直接読み取ったり変更したりする必要があるとき
  • スクロール位置を制御したりアニメーション効果を付けたいとき
  • 外部ライブラリ(LottieやChart.jsなど)でDOM要素を要求されるとき

このような場合にrefを使用すると、該当のDOM要素やコンポーネントを直接参照できるため、より簡単に制御することが可能になります。

なぜReact 18以前では forwardRef が必要だったのか?

Reactでは、refはDOM要素やコンポーネントインスタンスを直接参照するために使用されます。しかし、React 18以前では関数型コンポーネントがrefを通常のpropsのように直接受け取ることができませんでした。

たとえば、以下のようなシンプルなボタンコンポーネントがあるとしましょう

const MyButton = (props) => {
  return <button {...props}>{props.children}</button>;
};

そして、AppコンポーネントからこのMyButtonコンポーネントにrefを渡すとしましょう

const buttonRef = React.useRef();
<MyButton ref={buttonRef} />;

refは通常のpropsのようにMyButtonコンポーネントの引数として渡されるわけではありません。

なぜなら、Reactはrefを特別な経路で処理するためです。そのため、MyButton内部でprops.refのようにしてもrefの値を受け取ることができません。

その結果、refがbutton要素に届かなくなってしまうのです。

解決策:forwardRef

この問題を解決するために登場したのが、forwardRefです。
forwardRefは、外部から渡されたrefを、任意のDOM要素に「転送(forward)」する役割を持ちます。

import React, { forwardRef } from 'react';

const MyButton = forwardRef((props, ref) => (
  <button ref={ref} {...props}>
    {props.children}
  </button>
));

これで、MyButtonはrefを直接受け取ることができ、そのrefを<button>要素に安全に付与することができます。

そして、このように使用できます

const App = () => {
  const buttonRef = React.useRef();

  useEffect(() => {
    buttonRef.current.focus(); 
  }, []);

  return <MyButton ref={buttonRef}>Click Me</MyButton>;
};

forwardRefの不便な点

forwardRefは便利ですが、実際に使ってみると次のような欠点があります。

  1. コードが複雑になる
    forwardRefを使うと、通常の関数コンポーネントとは文法が異なるため、初心者にとっては混乱しやすくなります。
    また、forwardRefは高階関数(Higher-Order Function)なので、コンポーネントの定義が直感的に理解しにくくなる場合もあります。
const MyComponent = forwardRef((props, ref) => { ... });
  1. TypeScriptでは型指定がややこしい
    TypeScriptでは、propsとrefそれぞれの型を正確に指定する必要があるため、コードがさらに複雑になります。
type MyButtonProps = {
  children: React.ReactNode;
};

const MyButton = forwardRef<HTMLButtonElement, MyButtonProps>(
  (props, ref) => {
    return <button ref={ref} {...props} />;
  }
);
  1. 他のHOCやコンポーネントのラッピングと衝突する可能性がある
    forwardRefは、他の高階コンポーネント(HOC)と併用する際に、displayNameが失われるなど、デバッグが難しくなることがあります。

ラップが重なっていくと、どのrefがどこに渡されているのか追跡しづらくなることもあります。

React 19からはforwardRef不要

React 19では、ついに関数型コンポーネントでもrefを通常のpropsのように受け取れるように改善されました。
つまり、forwardRefを使わなくてもよくなり、コードがよりシンプルになりました。

const MyButton = ({ ref, ...props }) => {
  return (
    <button ref={ref} {...props}>
      {props.children}
    </button>
  );
};

const App = () => {
  const buttonRef = React.useRef();
  return <MyButton ref={buttonRef}>Click Me</MyButton>;
};

forwardRefを使わなくても、refが MyButton → <button> へ自然に渡されるようになりました。
これはReactの内部アーキテクチャが改善されたことによって可能になった変化であり、コードがより直感的でスッキリ書けるという点で大きな利点です。

Discussion