🦁

【React】タグと型ガードを利用してドメインを意識した表示切替

2024/10/21に公開

ユースケース

HeaderやFooterのようなコンポーネントで、ステータスや権限によってUIを変えたい場合、コンポーネントを分けるほどではないことがあります。そのような場合、propsで表示内容を切り替える方法が便利です。

例えば、次のようなHeaderPropsを定義したとします。

type HeaderProps = {
  title: string;
  leftButton?: React.ReactNode;
  rightButton?: React.ReactNode;
  onPress?: () => void;
};

今回の仕様は以下の通りです。

  • admin権限の場合、ボタンを左に寄せる
  • user権限の場合、ボタンを右に寄せる

この場合、別々のコンポーネントを作成するのではなく、propsによってUIの表示を切り替えることが多いと思います。

ドメインを意識した表示切替とは

「ユースケース」で紹介した仕様に基づいて、紹介します。

leftButtonとrightButtonをオプショナルプロパティとして定義した場合、次のようなtsxで実装することが考えられます。

{leftButton != null && props.leftButton}
{rightButton != null && props.rightButton}

しかし、この実装では、leftButtonとrightButtonがどの条件下で表示されるかが明確に表現されていません
これを改善し、以下の仕様に基づいてボタンの存在条件を、コードで分かりやすく示したいです。

  • admin権限のときはleftButtonが表示される
  • user権限のときはrightButtonが表示される

ドメインを意識した表示切替の手順

  1. コンポーネントにタグを付ける
  2. 型ガードを使用する

順に説明します。

1. コンポーネントにタグを付ける

権限に応じてtagを設定し、以下のように型定義を行います。

型定義
type Tag = 'admin' | 'user';

type BaseHeaderProps = {
  tag: Tag;
  title: string;
  onPress: () => void;
};

type AdminHeaderProps = BaseHeaderProps & {
  tag: 'admin';
  leftButton: React.ReactNode;
};

type UserHeaderProps = BaseHeaderProps & {
  tag: 'user';
  rightButton: React.ReactNode;
};

type HeaderProps = AdminHeaderProps | UserHeaderProps;

これにより、admin権限のときはleftButtonを渡し、user権限のときはrightButtonを渡すという仕様が明示的に型で表現できるようになりました。

コンポーネントを使う側では以下のように記述でき、どのボタンが表示されるか一目でわかるようになります。

コンポーネント使用する側
<Header tag="admin" leftButton={<button>left</button>} />
<Header tag="user" rightButton={<button>right</button>} />

2. 型ガードを使用する

Headerコンポーネント内部では、型ガードを使ってUIを切り替えることができます。

型ガードを利用することで、権限に応じたUIの切り替えを、Headerコンポーネント内でも明示的に表現できます。

const isAdmin = props.tag === "admin";
const isUser = props.tag === "user";

{isAdmin && props.leftButton}
{isUser && props.rightButton}

このように、UIをシンプルに切り替えられ、コードの意図がわかりやすくなります。

注意点

propsの条件分岐が複雑になったり、UIやロジックが複雑な場合は、AdminHeaderやUserHeaderのようにコンポーネントを分けることをお勧めします。
その方が、可読性や保守性が向上し、コードの理解が容易になります。

最後に

TypeScriptの力を使って、ドメインに基づいたコンポーネントが書けました。
今後も試行錯誤して良い書き方を探求していきます♫

今回紹介した一連の内容は、以下のリポジトリで実装しているので、ご参考になれば幸いです。

https://github.com/Yoshiyuki-Sato-1027/blog_samplecode/tree/header-tag-props-typeguard-ui?tab=readme-ov-file

エックスポイントワン技術ブログ

Discussion