目次
7 章

条件付きレンダリング

if・三項・&&・null 返しで UI を分けるパターンとよくある落とし穴 — 特に `&&` の左側の数値 0 の罠を整理します。

6 章の最後に {lastSubmitted && ...} のような表現をちらりと見ました。本章では画面を state に応じて違うように描く条件付きレンダリング(Conditional Rendering) のパターンを整理します。

条件付きレンダリングとは #

ログイン状態に応じて違うメニューを見せたり、データを取得している間ローディング表示を出したり、入力検証に失敗したときにエラーメッセージを見せたりすることは、すべてのアプリで起きます。ある条件に応じて違う JSX をレンダリングすることを条件付きレンダリングといいます。

React では別途の構文があるわけではなく、JavaScript の条件表現をそのまま活用します。最もよく使われる 4 つのパターンを見ていきます。

パターン 1. if 文で分岐(early return) #

最も直感的な方法は、コンポーネント関数の中で if で分岐し、違う JSX を返すことです。

src/Greeting.jsx
function Greeting({ user }) {
  if (!user) {
    return <p>ログインが必要です</p>;
  }

  return <h1>こんにちは{user.name}さん!</h1>;
}

export default Greeting;

user がなければログイン案内を返して関数が終わります。2 番目の returnuser があるときだけ実行されます。こうして関数を早めに抜ける方式を early return と呼び、分岐が大きいときに読みやすいです。

パターン 2. 三項演算子(JSX の中で 2 つに分ける) #

JSX の真ん中で 2 つのうちのどちらかを選ぶ必要があるなら、JavaScript の三項演算子(条件 ? A : B)を使います。

src/LoginButton.jsx
function LoginButton({ isLoggedIn }) {
  return (
    <button>
      {isLoggedIn ? 'ログアウト' : 'ログイン'}
    </button>
  );
}

export default LoginButton;

JSX の波括弧の中にはだけが入る、という点を覚えていますか。if 文は文(statement)なので入れられませんが、三項演算子は式なので入れられます。

JSX 自体を 2 つに分けるのにも使えます。

src/UserStatus.jsx
function UserStatus({ user }) {
  return (
    <div>
      {user ? (
        <p>こんにちは{user.name}さん!</p>
      ) : (
        <p>ログインが必要です</p>
      )}
    </div>
  );
}

ただし三項が長くなると読みにくくなるので、JSX の塊が大きければパターン 1(early return)や変数に分ける方がよいです。

パターン 3. && 演算子(見えるか見えないか) #

「条件が真のときだけ何かを見せ、偽なら何も見せない」というケースが最も多いです。このときは && 演算子がよく合います。

src/Notification.jsx
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 は falsenullundefined を画面に何も描かないので、結果的に消えたように見えます。

&& 使用時の落とし穴 — 数値 0 #

&& のよくある落とし穴が 1 つあります。次のコードを見てください。

問題のあるコード
function Cart({ count }) {
  return (
    <div>
      {count && <p>カートに {count} 個入っています</p>}
    </div>
  );
}

count が 0 のとき意図は「何も見えないこと」ですが、実際には画面に 0 という数字がそのまま出力されます。0 && X0 を返すからで、React は数値 0 は画面に本当に描きます(falsenullundefined だけが描かれません)。

解決策は明示的に真偽値に変えることです。

修正したコード
{count > 0 && <p>カートに {count} 個入っています</p>}

count > 0 は常に true または false なので安全です。&& の左側に明確な真偽値を置く習慣を付ければ、この罠にハマりません。空文字列 '' も同じ罠があるので、name && ... の代わりに name.length > 0 && ... のような明確な比較を使う方が安全です。

パターン 4. null 返し(コンポーネント自体を描かない) #

条件が満たされなければコンポーネント全体が画面に現れてはならないときは、null を返します。

src/Banner.jsx
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 を変数に入れておいてその変数を使う方が読みやすいです。

src/Page.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 を拡張してみます。「名前を入力しなければ警告表示」「メッセージを入力しなければ警告表示」「両方とも問題なければ送信可能」になるようにします。

src/MessageForm.jsx
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 つの画面で自然に混ざって使われる例です。

練習問題 #

  1. Page コンポーネントを作り、status prop('loading' / 'error' / 'success')に応じて違う画面を描くようにしてみてください。'loading' なら「読み込み中」、'error' なら赤色のエラーメッセージ、'success' ならデータ表示。early return / 三項 / 変数に JSX を入れる、の 3 つの方式でそれぞれ書いてみて、どれが最も読みやすいか比較してみてください。
  2. && の罠を直接体験します。count state を 0 から始めるカウンタで {count && <p>カート: {count}</p>} パターンを使ってみてください。最初の画面に意図と違って 0 が表示されるはずです。その後 count > 0 && ... に直して罠から抜け出してください。
  3. Banner コンポーネントを作り、message prop があれば黄色のボックスで表示し、なければ null 返しになるように書いてみてください。親で <Banner message={errorMessage} /> のように常に呼び出しておき、errorMessage state が空になったり埋まったりする流れで自然に見えたり消えたりするかを確認します。

一行まとめ: React の条件付きレンダリングは JavaScript の条件表現そのままだ。early return / 三項 / && / null 返し / 変数に JSX を入れる — この 5 つのパターンを状況に応じて選ぶ。&& の左側には明確な真偽値を置いて数値 0 の罠を避ける。

次の章 #

ここまでは画面に見せるデータが 1 つか 2 つに限られていました。実際のアプリでは投稿リスト、商品リスト、通知リストのように複数のデータを一度に描く必要があります。次の第 8 章 リストと keyでは、配列を画面に描く方法と、そのときに必ず登場する key という特別な prop の意味を扱います。14 章(パフォーマンス最適化)の reconciliation アルゴリズムの話への土台になります。

X