50
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React.ComponentProps で Props の受け渡しを楽にする

Last updated at Posted at 2020-06-08

React×Typescript でよくあるのが 「同じ Props の型宣言を何度も書くのが面倒くさい!」 ということ。
しかし ComponentProps を使用すれば子コンポーネントの Props の型を参照できるので、簡潔に記述することができます。

ComponentProps を使ってみる

全部の型を参照

例えば以下のような画像を表示するだけの処理があるとします。

Item.tsx
import React, { FC } from 'react';

type Props = {
  image: {
    id: number;
    url: string;
  };
};

export const Item: FC<Props> = ({ image }) => (
  <img src={image.url} alt={String(image.id)} />
);

このコンポーネントを呼び出す場合、親にも同じ型宣言が必要になります。

List.tsx
import React, { ComponentProps } from 'react';
import { Item } from 'Item';

type Props = {
  image: {
    id: number;
    url: string;
  };
};

export const List = ({ image }: Props) => (
  <Item image={image} />
)

面倒ですね。

そこで登場するのが ComponentProps
ComponentProps を使えば、上記 List.tsx の Props 内を ComponentProps で置き換えることができます。

List.tsx
import React from 'react';
import { Item } from 'Item';

type Props = React.ComponentProps<typeof Item>;

export const List = ({ image }: Props) => (
  <Item image={image} />
)

ComponentProps<typeof [コンポーネント]>
と指定することで、対象コンポーネントに指定されている型を参照することができます。
今回の場合は Item.tsxexport const Item: FC<Props> = ... とジェネリクスに Props が指定されているので、Props を参照していることになります。

一部の型を参照

型を一部だけ参照することも可能です。
例えば下記 Props の中身から image だけ参照したい場合もあります。

type Props = {
  image: {
    id: number;
    url: string;
  };
  user: {
    id: number;
    name: string;
  };
};

その場合は、ComponentProps<typeof Item> の後にさらに参照したい部分だけ記述するといいでしょう。

type Props = {
  image: React.ComponentProps<typeof Item>['image'];
};

型の拡張・縮小を行う

ComponentProps で型の拡張・縮小も楽に行うことができます。

が、その前にまずは Typescript における type の操作方法について見ていきましょう。

Type の操作方法

型の結合

型をそのまま結合します。

type A = {
  a1: string;
  a2: number;
};

type B = {
  b1: number;
  b2: string;
}

type AB = A & B;

// ↑の内容
type AB = {
  a1: string;
  a2: number;
  b1: number;
  b2: string;
}

型の抜き出し

指定した型のみで構築します。

type ABPick = Pick<AB, 'a1' | 'b1'>;

// ↑の内容
type ABPick = {
  a1: string;
  b1: number;
}

型の削除

指定した型を削除して構築します。

type ABOmit = Omit<AB, 'a1' | 'b1'>;

// ↑の内容
type ABPick = {
  a2: number;
  b2: string;
}

よく使いそうな物を紹介しました。
他にも Partial など沢山あるので、詳しくはこちらを参照してください。
https://www.typescriptlang.org/docs/handbook/utility-types.html

ComponentProps の利用

それでは ComponentProps を利用します。

まずは上記 Item.tsx に要素を追加したくなったので、下記のように User コンポーネントを追加したとしましょう。

Item.tsx
import React, { FC } from 'react';
import { User } from 'User';

type Props = {
  image: {
    id: number;
    url: string;
  };
  user: {
    id: number;
    name: string;
  };
};

export const Item: FC<Props> = ({ image, user }) => (
  <>
    <img src={image.url} alt={String(image.id)} />
    <User user={user} />
  </>
);

ここで User の型宣言を ComponentProps に変えて、
type の型結合を行えば下記のように すっきりした記述になります。

Item.tsx
import React, { FC } from 'react';
import { User } from 'User';

// ComponentProps を利用する
type UserProps = React.ComponentProps<typeof User>;

type Props = {
  image: {
    id: number;
    url: string;
  };
} & UserProps; // UserProps を Props と結合する

export const Item: FC<Props> = ({ image, user }) => (
  <>
    <img src={image.url} alt={String(image.id)} />
    <User user={user} />
  </>
);

もし User の型宣言が

type Props = {
  user: {
    id: number;
    name: string;
  };
  avatar: {
    url: string;
  }
};

のようになっていて、user 部分だけ結合したいという場合は
Props 部分は下記のようになるでしょう。

type Props = {
  image: {
    id: number;
    url: string;
  };
} & Pick<UserProps, 'user'>;

まとめ

ComponentProps を利用すればかなり快適に Props の受け渡しが楽になるのでおすすめです。

50
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?