条件付きレンダリング
if・三項・&&・null 返しで UI を分けるパターンとよくある落とし穴 — 特に `&&` の左側の数値 0 の罠を整理します。
6 章の最後に {lastSubmitted && ...} のような表現をちらりと見ました。本章では画面を state に応じて違うように描く条件付きレンダリング(Conditional Rendering) のパターンを整理します。
条件付きレンダリングとは #
ログイン状態に応じて違うメニューを見せたり、データを取得している間ローディング表示を出したり、入力検証に失敗したときにエラーメッセージを見せたりすることは、すべてのアプリで起きます。ある条件に応じて違う JSX をレンダリングすることを条件付きレンダリングといいます。
React では別途の構文があるわけではなく、JavaScript の条件表現をそのまま活用します。最もよく使われる 4 つのパターンを見ていきます。
パターン 1. if 文で分岐(early return) #
最も直感的な方法は、コンポーネント関数の中で if で分岐し、違う JSX を返すことです。
function Greeting({ user }) {
if (!user) {
return <p>ログインが必要です。</p>;
}
return <h1>こんにちは、{user.name}さん!</h1>;
}
export default Greeting;user がなければログイン案内を返して関数が終わります。2 番目の return は user があるときだけ実行されます。こうして関数を早めに抜ける方式を early return と呼び、分岐が大きいときに読みやすいです。
パターン 2. 三項演算子(JSX の中で 2 つに分ける) #
JSX の真ん中で 2 つのうちのどちらかを選ぶ必要があるなら、JavaScript の三項演算子(条件 ? A : B)を使います。
function LoginButton({ isLoggedIn }) {
return (
<button>
{isLoggedIn ? 'ログアウト' : 'ログイン'}
</button>
);
}
export default LoginButton;JSX の波括弧の中には式だけが入る、という点を覚えていますか。if 文は文(statement)なので入れられませんが、三項演算子は式なので入れられます。
JSX 自体を 2 つに分けるのにも使えます。
function UserStatus({ user }) {
return (
<div>
{user ? (
<p>こんにちは、{user.name}さん!</p>
) : (
<p>ログインが必要です。</p>
)}
</div>
);
}ただし三項が長くなると読みにくくなるので、JSX の塊が大きければパターン 1(early return)や変数に分ける方がよいです。
パターン 3. && 演算子(見えるか見えないか) #
「条件が真のときだけ何かを見せ、偽なら何も見せない」というケースが最も多いです。このときは && 演算子がよく合います。
function Notification({ unreadCount }) {
return (
<div>
<h2>通知</h2>
{unreadCount > 0 && (
<p>未読メッセージが {unreadCount} 件あります。</p>
)}
</div>
);
}
export default Notification;A && B は JavaScript で A が真なら B を返し、偽なら A を返します。unreadCount > 0 が真なら <p>...</p> がその位置に入り、偽なら false が入ります。React は false、null、undefined を画面に何も描かないので、結果的に消えたように見えます。
&& 使用時の落とし穴 — 数値 0 #
&& のよくある落とし穴が 1 つあります。次のコードを見てください。
function Cart({ count }) {
return (
<div>
{count && <p>カートに {count} 個入っています。</p>}
</div>
);
}count が 0 のとき意図は「何も見えないこと」ですが、実際には画面に 0 という数字がそのまま出力されます。0 && X は 0 を返すからで、React は数値 0 は画面に本当に描きます(false、null、undefined だけが描かれません)。
解決策は明示的に真偽値に変えることです。
{count > 0 && <p>カートに {count} 個入っています。</p>}count > 0 は常に true または false なので安全です。&& の左側に明確な真偽値を置く習慣を付ければ、この罠にハマりません。空文字列 '' も同じ罠があるので、name && ... の代わりに name.length > 0 && ... のような明確な比較を使う方が安全です。
パターン 4. null 返し(コンポーネント自体を描かない) #
条件が満たされなければコンポーネント全体が画面に現れてはならないときは、null を返します。
function Banner({ message }) {
if (!message) return null;
return (
<div style={{ background: '#fffbcc', padding: '12px' }}>
{message}
</div>
);
}
export default Banner;null を返すと、React はそのコンポーネントの位置に何も描きません。親の立場では <Banner message={...} /> と常に書いておけばよく、見せるかどうかは Banner 自身が決める構造です。きれいです。
変数に JSX を入れておく #
分岐が複雑になったら、JSX を変数に入れておいてその変数を使う方が読みやすいです。
function Page({ status, data, error }) {
let content;
if (status === 'loading') {
content = <p>読み込み中...</p>;
} else if (status === 'error') {
content = <p style={{ color: 'red' }}>エラー: {error}</p>;
} else {
content = <p>データ: {data}</p>;
}
return (
<div>
<h1>ページタイトル</h1>
{content}
</div>
);
}JSX はただの JavaScript の値なので、変数に入れたり、関数から返したり、オブジェクトに入れたりできます。
パターン整理 #
| 状況 | 推奨パターン |
|---|---|
| 分岐結果がコンポーネント全体の JSX の場合 | early return(if) |
| 2 つのうちのどちらかを見せたい場合 | 三項演算子(A ? B : C) |
| 条件が真のときだけ見せたい場合 | && 演算子(左側は真偽値で) |
| コンポーネントがそもそも見えてはならない場合 | return null |
| 分岐が 3 つ以上または複雑な場合 | 変数に JSX を入れて使う |
React 固有の特別な構文があるのではなく、JavaScript の条件表現を JSX の中でそのまま使うという点が核心です。
自分でやってみる #
6 章で作った MessageForm を拡張してみます。「名前を入力しなければ警告表示」「メッセージを入力しなければ警告表示」「両方とも問題なければ送信可能」になるようにします。
import { useState } from 'react';
function MessageForm() {
const [name, setName] = useState('');
const [message, setMessage] = useState('');
const [lastSubmitted, setLastSubmitted] = useState(null);
const isValid = name.length > 0 && message.length > 0;
function handleSubmit(e) {
e.preventDefault();
if (!isValid) return;
setLastSubmitted({ name, message });
setName('');
setMessage('');
}
return (
<div style={{ padding: '16px', border: '1px solid #ccc', borderRadius: '8px' }}>
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
placeholder="名前"
value={name}
onChange={(e) => setName(e.target.value)}
/>
{name.length === 0 && (
<span style={{ color: 'red', marginLeft: '8px' }}>名前を入力してください</span>
)}
</div>
<div style={{ marginTop: '8px' }}>
<input
type="text"
placeholder="メッセージ"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
{message.length === 0 && (
<span style={{ color: 'red', marginLeft: '8px' }}>メッセージを入力してください</span>
)}
</div>
<button type="submit" disabled={!isValid} style={{ marginTop: '8px' }}>
{isValid ? '追加' : '入力を完了してください'}
</button>
</form>
{lastSubmitted ? (
<p style={{ marginTop: '12px' }}>
最後の入力: <strong>{lastSubmitted.name}</strong>. {lastSubmitted.message}
</p>
) : (
<p style={{ marginTop: '12px', color: '#888' }}>
まだ送信されたメッセージがありません。
</p>
)}
</div>
);
}
export default MessageForm;ここで使った条件付きレンダリングのパターンたち:
name.length === 0 && <span>...</span>—&&で入力されていないときだけ警告表示disabled={!isValid}— 妥当性に応じてボタンの活性 / 非活性isValid ? '追加' : '入力を完了してください'— 三項でボタンテキストを分岐lastSubmitted ? <p>...</p> : <p>...</p>— 三項で案内メッセージを分岐
複数のパターンが 1 つの画面で自然に混ざって使われる例です。
練習問題 #
Pageコンポーネントを作り、statusprop('loading'/'error'/'success')に応じて違う画面を描くようにしてみてください。'loading'なら「読み込み中」、'error'なら赤色のエラーメッセージ、'success'ならデータ表示。early return / 三項 / 変数に JSX を入れる、の 3 つの方式でそれぞれ書いてみて、どれが最も読みやすいか比較してみてください。&&の罠を直接体験します。countstate を 0 から始めるカウンタで{count && <p>カート: {count}</p>}パターンを使ってみてください。最初の画面に意図と違って0が表示されるはずです。その後count > 0 && ...に直して罠から抜け出してください。Bannerコンポーネントを作り、messageprop があれば黄色のボックスで表示し、なければnull返しになるように書いてみてください。親で<Banner message={errorMessage} />のように常に呼び出しておき、errorMessagestate が空になったり埋まったりする流れで自然に見えたり消えたりするかを確認します。
一行まとめ: React の条件付きレンダリングは JavaScript の条件表現そのままだ。early return / 三項 /
&&/null返し / 変数に JSX を入れる — この 5 つのパターンを状況に応じて選ぶ。&&の左側には明確な真偽値を置いて数値 0 の罠を避ける。
次の章 #
ここまでは画面に見せるデータが 1 つか 2 つに限られていました。実際のアプリでは投稿リスト、商品リスト、通知リストのように複数のデータを一度に描く必要があります。次の第 8 章 リストと keyでは、配列を画面に描く方法と、そのときに必ず登場する key という特別な prop の意味を扱います。14 章(パフォーマンス最適化)の reconciliation アルゴリズムの話への土台になります。