【React】タグと型ガードを利用してドメインを意識した表示切替
ユースケース
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. コンポーネントにタグを付ける
権限に応じて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の力を使って、ドメインに基づいたコンポーネントが書けました。
今後も試行錯誤して良い書き方を探求していきます♫
今回紹介した一連の内容は、以下のリポジトリで実装しているので、ご参考になれば幸いです。
Discussion