stin's Blog

React.FCを使うよ、理に適っているからね


唐突に、React.FCの是非についてポジションを示そうと思ったので書きます。積極的に使う派閥です。

React.FC vs Normal Function

Reactで関数コンポーネントを宣言するとき、TypeScriptの型の付け方として大きく次のような2つの書き方があります。

  1. React.FCを使うパターン

    コンポーネントが受け取るPropsとReact.FCを使ってコンポーネントの型を定義します。

    type Props = {
      name: string;
    };
    
    const MyComponent: React.FC<Props> = ({ name }) => {
      return <h1>Hello {name}!</h1>;
    };
  2. 引数型と戻り値型を個別に指定するパターン

    関数の引数と戻り値の型を個別に指定した関数としてコンポーネントを定義します。アロー関数だったりfunctionキーワードだったりしますが、Reactコンポーネントにおいて違いはないので同じとみなします。

    type Props = {
      name: string;
    };
    
    const MyComponent3 = ({ name }: Props): React.JSX.Element => {
      return <h1>Hello {name}!</h1>;
    };

過去に見たReact.FC使わない派の意見

  • create-react-app のテンプレートが使わなくなったから
  • ジェネリクス(型パラメーター)が使えないから
  • Next.jsなどのサンプルコードやソースコードで使用されていないから
  • 使うつもりがないchildrenが勝手に許容されるから(すでに修正済み)

自分が積極的に使う理由

1つの型だけでReactコンポーネントであることが確定できるからです。

Reactコンポーネントは、ただ一つのオブジェクト型引数(つまりProps)とReact要素妥当なJSXや文字列、数値などを戻り値とする関数です。型と戻り値がセットで一致して初めてReactコンポーネントとして認められます。

引数と戻り値の型を2つセットで定義する必要がある以上、それを同時に宣言できるReact.FCはお誂え向きと言えます。通常の関数型宣言ではどちらかを間違えてしまうかもしれません。

また、Reactコンポーネントの戻り値として妥当な型は、色々あってどれを使えば良いかよくわからないという問題もあります。React.ReactNode , JSX.Element, React.JSX.Element, React.ReactElement...。どれを選んでもTypeScriptはReactコンポーネントとして認識しますが、どれを使うべきかは知りません。そんなことに迷うよりは、React.FC一発で決めればよいでしょう。

ちなみに、JSX.Elementは少し前にdeparecatedになりました。今後はReact.JSX.Elementが代替となります。JSX自体はReact非依存のシンタックスなので、名前空間の調整をするというのが目的でしょうかね。

名前の明示としても優秀です。FCはFunction Componentの省略で、まさに「Reactの関数コンポーネントであること」を型で説明しています。これは可読性に大きく影響を与えます。

使わない派の意見への反論

「各種スターターの初期セットやツールのサンプルコードで使われていないから」については、他人の意見に従うだけで自分の考えを持たない愚者の意見です。React.FCがdeprecatedになったわけでもありませんので、それらのコードで使われていないのはそれらの開発者の好みでしょう。自分で考えてReact.FCを使う理由を見出していれば、そのような他者のコードにいちいち振り回される必要もありません。

「ジェネリクスが使えない」というのは事実ですが、大した問題ではないです。Propsの渡し方で複雑に型を変えたいケースは、アプリケーション開発においては経験上多くないです。シンプルなPropsか、せいぜいユニオン型で表現できる程度のPropsで済むでしょう。むしろジェネリクスを駆使してまで複雑な型定義がほしいとき、何か詰め込みすぎていないかをまず疑うべきです。

仮にジェネリクスを使うことになったとしても、そのときだけReact.FCを使わずに型定義すれば済む話です。React.FCではジェネリクスコンポーネントを作れないからといって、すべてのコンポーネント定義でReact.FCを使わない理由にはなりません。基本はReact.FCを使い、ジェネリクスが必要になったときだけReact.FCを外します。

childrenについて、React 17までの型定義(DefinitelyTyped)ではReact.FCで関数コンポーネントを宣言するとなぜか勝手にchildrenも渡せるようなProps型になっていました。しかしこれはReact 18用の型定義で削除されたので今となってはReact.FCの否定根拠になりません。

プロジェクトには合わせる

プロジェクトの思想には合わせるつもりです。プロジェクトがReact.FCを使わずに実装されているならば、それに従うでしょう。

もちろんそのプロジェクトは機械的にReact.FCを禁止するeslint ruleが設定されているはずですよね?

まとめ

僕はReact.FCを積極的に使用します。関数コンポーネントに対する型定義を一発で、名前の明示もでき、理に適っているからです。

それでは良いReactライフを!