サイト名変更・お引越しのお知らせ

【React・TypeScript】VFCもしくはFCそれともなし?

VFCを使うべきか、FCを使うべきか、それとも不要かを整理していきたいと思います。

バージョン
  • react 18.2.0

VFC・FCとは?

Functional Componentの型のことです。

FCだとこんな感じで定義してあり、戻り値の指定といくつかのフィールドを持っています。

type FC<P = {}> = FunctionComponent<P>;

interface FunctionComponent<P = {}> {
  (props: P, context?: any): ReactElement<any, any> | null;
  propTypes?: WeakValidationMap<P> | undefined;
  contextTypes?: ValidationMap<any> | undefined;
  defaultProps?: Partial<P> | undefined;
  displayName?: string | undefined;
}

FCの昔(v18以前)

FCはchildrenが定義してあったので、childrenが不要なコンポーネントにchildrenを渡してもエラーが出ないという問題点がありました。

TypeScriptで型の抜け道は良くないですよね。

interface Props {
  name: string;
}

const SomeComponent: React.FC<Props> = ({ name }) => (
  <p>{name}</p>;
);

const ParentComponent: React.FC = () => (
  <SomeComponent name="sam">
    children
  </Component>
);

VFCの登場

そこで、VFCという型が登場しました。

VFCにはchildrenが存在していないので定義していない場合にはエラーが表示されました。

ERROR: Type '{ children: string; name: string; }' is not assignable to type...

interface Props {
  name: string;
}

const SomeComponent: React.VFC<Props> = ({ name }) => (
  <p>{name}</p>;
);

const ParentComponent: React.VFC = () => (
  <SomeComponent name="sam">
    children
  </Component>
);

childrenを渡すには明示的に定義すれば渡すことができます

interface Props {
  name: string;
  children: React.ReactNode;
}

const SomeComponent: React.VFC<Props> = ({ name, children }) => (
  <div>
    <p>{name}</p>;
    <p>{children}</p>;
  </div>
);

const ParentComponent: React.VFC = () => (
  <SomeComponent name="sam">
    children
  </Component>
);

モグモグさん

追加された実際のPRはこちらです。

現在のVFCとFC

というわけで、v18より前はVFCは結構使われていましたが、v18からはFCからchildrenが除かれました。

つまり、FCとVFCは同じ型になったということです。

それによって、VFCはdeprecatedになりました。

// React.FunctionComponent
type FC<P = {}> = FunctionComponent<P>;
interface FunctionComponent<P = {}> {
  (props: P, context?: any): ReactElement<any, any> | null;
  propTypes?: WeakValidationMap<P> | undefined;
  contextTypes?: ValidationMap<any> | undefined;
  defaultProps?: Partial<P> | undefined;
  displayName?: string | undefined;
}

// @deprecated - Equivalent with `React.FC`.
type VFC<P = {}> = VoidFunctionComponent<P>;
interface VoidFunctionComponent<P = {}> {
  (props: P, context?: any): ReactElement<any, any> | null;
  propTypes?: WeakValidationMap<P> | undefined;
  contextTypes?: ValidationMap<any> | undefined;
  defaultProps?: Partial<P> | undefined;
  displayName?: string | undefined;
}

モグモグさん

結論: 現在はVFCではなくFCを使うことが推奨されています。

FCは使うべき?

VFCは使わなくなったわけですが、そもそもFCを使うべきなのかどうなのか。

JSX.Elementとの違いは、nullを返せるか定義されたフィールドがあるかどうかの違いです。

namespace JSX {
  interface Element extends React.ReactElement<any, any> { }

propTypesdefaultPropsは使わなそうですが、displayNameはプロジェクトによっては使います。

モグモグさん

結論: プロジェクトの性質や書き方、個人やチームの方針で決めるでいいかなと思いました。

ただ「一応使っておこう」の場合は当てはまるかはわからないですが、YAGNI原則に則って不要なんじゃないかなと思っております。

FCに変更が入れば対応するコストもかかる可能性も増えますよね。

まとめ

VFCを使うべきか、FCを使うべきか、それとも不要かを整理しました。

v18からはVFCは非推奨で使うべきではないです。

FCを使うか使わないかは違いを理解して、プロジェクトの性質や書き方、個人やチームの方針で決めるのがいいのではと個人的には思っています。

VFCとFCでちょっと混乱している方に役に立てば幸いです。