こんにちは、かつコーチです。
前回はイベントハンドリングを使って、ユーザー操作から状態を更新する方法を解説しました。
状態が扱えるようになると、次に欲しくなるのが「状態によって表示を切り替える」機能です。
ログイン状態によってボタンを出し分けたり、ローディング中はスピナーを表示したりする場面は非常によくあります。
今回は、React特有の条件分岐レンダリングのパターンをまとめて紹介します。
条件分岐レンダリングとは?
状態に応じて表示するJSXを切り替える仕組み
条件分岐レンダリングとは、状態やPropsの値に応じて、表示するJSXの内容を切り替えることです。
ReactにはVueの v-if のような専用の構文はなく、素のJavaScriptの条件分岐(if や三項演算子)をJSXの中で活用します。
「JSXはただのJavaScriptの式」という感覚を持っておくと、この後の各パターンが理解しやすくなります。
なぜパターンを知っておく必要があるのか
条件分岐の書き方は1つではなく、状況によって適切な方法が異なります。
パターンを知らないと、無理やり複雑な三項演算子をネストさせて読みにくいコードになりがちです。
代表的な書き方を知っておくことで、場面に応じて読みやすい方法を選べるようになります。
基本パターン
パターン1:関数内でif文を使い、早期リターンする
コンポーネント全体をまるごと出し分けたい場合は、レンダリング前に if 文で早期リターンするのが読みやすいです。
interface Props {
isLoading: boolean;
}
function UserProfile({ isLoading }: Props) {
if (isLoading) {
return <p>読み込み中です...</p>;
}
return <p>プロフィール情報を表示します</p>;
}
return文をJSXの中に混ぜずに、コンポーネントの先頭で条件分岐を終わらせてしまう書き方です。
分岐後のJSXがそれぞれシンプルになるので、可読性が高くなります。
パターン2:三項演算子でインラインに出し分ける
JSXの中で短く出し分けたい場合は、三項演算子 条件 ? A : B が便利です。
function LoginButton({ isLoggedIn }: { isLoggedIn: boolean }) {
return (
<div>
{isLoggedIn ? (
<button>ログアウト</button>
) : (
<button>ログイン</button>
)}
</div>
);
}
「trueならA、falseならB」という2択の表示切り替えに向いています。
ただし、ネストして三項演算子を重ねると一気に読みにくくなるので、2択を超える場合は別のパターンを検討したほうがよいです。
パターン3:&&演算子で「あるときだけ表示する」
「条件を満たすときだけ表示し、満たさないときは何も表示しない」場合は、&& 演算子がよく使われます。
function Notification({ hasNewMessage }: { hasNewMessage: boolean }) {
return (
<div>
<p>メッセージ一覧</p>
{hasNewMessage && <span>新着あり!</span>}
</div>
);
}
hasNewMessage が true のときだけ <span>新着あり!</span> が表示され、false のときは何もレンダリングされません。
これは「false &&〜 の場合、React が false を何も描画しないものとして扱う」という性質を利用したパターンです。
よくあるつまずきポイント:&&演算子で数値の0が表示される
実際にハマった落とし穴
私が実務で一度やってしまった失敗が、この && 演算子と数値を組み合わせたときのバグです。
❌ Before:件数が0のとき「0」が表示されてしまう
function CartBadge({ itemCount }: { itemCount: number }) {
return (
<div>
{itemCount && <span className="badge">{itemCount}件</span>}
</div>
);
}
一見問題なさそうに見えるのですが、itemCount が 0 のとき、JavaScriptでは 0 は falsy な値として扱われます。
しかし 0 && <span>...</span> の結果は false ではなく 0 そのものになります。
Reactは false や null は描画しませんが、0 は数値として画面にそのまま描画してしまうため、カートが空のはずなのに画面に 0 という文字だけがポツンと表示されてしまいました。
このバグはローカル環境ではなぜか気づかず、公開後にユーザーからの指摘で初めて発覚した苦い経験があります。
✅ After:booleanに変換してから判定する
function CartBadge({ itemCount }: { itemCount: number }) {
return (
<div>
{itemCount > 0 && <span className="badge">{itemCount}件</span>}
</div>
);
}
itemCount > 0 のように、明示的に真偽値(boolean)を返す条件式にすることで、0 が意図せず画面に表示される問題を防げます。
&& を使うときは、左辺が「必ずboolean型になっているか」を意識する癖をつけると、このバグを未然に防げます。
応用:一歩先の使い方
分岐が3パターン以上あるときはオブジェクトマップを使う
三項演算子や && は2〜3パターンまでが読みやすい限界です。
状態の種類が多い場合は、オブジェクトに対応するJSXをまとめておく方法がすっきりします。
type Status = "loading" | "success" | "error";
function StatusMessage({ status }: { status: Status }) {
const messages: Record<Status, React.ReactNode> = {
loading: <p>読み込み中です...</p>,
success: <p>読み込みが完了しました</p>,
error: <p>エラーが発生しました</p>,
};
return <div>{messages[status]}</div>;
}
Record<Status, React.ReactNode> という型で、status の値ごとに対応するJSXを1つのオブジェクトにまとめています。
if や三項演算子を何段も重ねるより、どの状態にどの表示が対応しているか一覧で把握しやすくなります。
条件分岐が複雑になったらコンポーネントを分割する
1つのコンポーネントの中でJSXの出し分けが増えすぎたら、思い切って別のコンポーネントに分割するのも有効な手段です。
function OrderStatus({ status }: { status: Status }) {
if (status === "loading") return <LoadingView />;
if (status === "error") return <ErrorView />;
return <SuccessView />;
}
条件分岐そのものをシンプルに保ちつつ、各状態の詳しい表示内容は別コンポーネントに任せることで、見通しのよいコードになります。
まとめ
この記事のポイント
- 条件分岐レンダリングは、状態やPropsに応じて表示するJSXを切り替える仕組み
- コンポーネント全体を出し分けるならif文の早期リターン、2択ならば三項演算子が読みやすい
&&演算子は「あるときだけ表示」に便利だが、左辺が数値の場合は0が表示されるバグに注意する- 分岐パターンが3つ以上になる場合は、オブジェクトマップでまとめると見通しがよくなる
- 条件分岐が複雑になったら、コンポーネント自体を分割することも検討する
次に読むべき記事
条件によって「表示するかどうか」を切り替えられるようになったら、次は複数のデータを一覧として表示する方法を学びます。
次回は、配列をレンダリングする際に欠かせない key 属性の重要性について解説します。
→ 次の記事:リスト表示とkey属性の重要性