React: 条件付きレンダリングとリストレンダリング

最終更新:2026-10-26

条件付きレンダリングは信号機のようなもので、状態に基づいて何を表示するかを決定します。一方、リストレンダリングは流れ作業のようなもので、データのリストから同一のUIコンポーネントをバッチ処理で生成します。


1. 学習内容



2. 電子商取引プラットフォームのフロントエンド事情

(1) 課題:商品リストの6つの状態

アリスは、あるECプラットフォームの商品一覧ページの制作に取り組んでいます。このページには6つの表示状態があり、状態が変わるたびに、彼女は手動でDOMを操作しなければなりません:

JAVASCRIPT
function renderProductList(state) {
  const container = document.getElementById('product-list')
  container.innerHTML = ''

  if (state.isLoading) {
    container.innerHTML = '<div class="loading">Loading......</div>'
  } else if (state.error) {
    container.innerHTML = '<div class="error">Failed to load</div>'
  } else if (!state.isLoggedIn) {
    container.innerHTML = '<div>Please log in</div>'
  } else if (state.products.length === 0) {
    container.innerHTML = '<div>No products available</div>'
  } else {
    let html = ''
    state.products.forEach(p => {
      html += `<div class="card"><h3>${p.name}</h3><p>$${p.price}</p></div>`
    })
    container.innerHTML = html
  }
}

アリスの質問:状態が変わるたびに、手動でコンテナをクリアしてHTMLを生成しなければなりません。たった30行のコードで、現在の状態を表示するというたった一つのことしかできていません。

(2) Reactにおける条件付きレンダリングの解決策

JSX
function ProductList({ user, products, isLoading, error }) {
  // Each state is independent if Branch,No interference with one another
  if (isLoading) return <LoadingSpinner />
  if (error) return <ErrorMessage message={error} />
  if (!user) return <LoginPrompt />
  if (products.length === 0) return <EmptyState />
  
  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}
▶ 試してみよう

メリット:わずか15行のコードですべての状態を網羅でき、各分岐が一目で理解しやすい。



3. 条件付きレンダリングの3つの活用方法

メソッド 構文 使用例 備考
三項演算子 condition ? A : B 2つの選択肢から1つを選ぶ(「else」分岐を含む) 三項演算子のネストは可読性を損なうため、2レベル以上は避ける
論理演算と && condition && A 条件が満たされた場合にのみ表示(「else」なし) 0 && <Comp /> は 0 を返すため、> 0 を使用してください
論理OR ` ` `value

(1) 三項演算子 ? :

「2つの選択肢から1つを選ぶ」という状況に適したシナリオ:

JSX
function StatusBadge({ isActive }) {
  return (
    <span style={{ color: isActive ? '#52c41a' : '#ff4d4f' }}>
      {isActive ? '🟢 Online' : '🔴 Offline'}
    </span>
  )
}

// isActive=true  → <span>🟢 Online</span>
// isActive=false → <span>🔴 Offline</span>
▶ 試してみよう

(2) 論理と &&

「条件が満たされた場合にのみ表示し、そうでない場合は表示しない」という用途に適しています:

JSX
function Notification({ count }) {
  return (
    <div className="notification-icon">
      🔔
      {/* count > 0 A red dot appears,Otherwise, nothing will be displayed. */}
      {count > 0 && (
        <span className="badge">{count > 99 ? '99+' : count}</span>
      )}
    </div>
  )
}

// count=0  → Show only 🔔
// count=5  → Show 🔔 and  "5"
▶ 試してみよう
⚠️ 注意点:{items.length && <List />} と記述しないでください。items.length が 0 の場合、0 && <List /> の結果は 0 となり、React はページ上に 0 をレンダリングしてしまいます。正しい記述方法は {items.length > 0 && <List />} です。

(3) 論理OR ||

「フォールバック値の提供」に適しています:

JSX
function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.nickname || 'Anonymous User'}</h2>
      <p>{user.bio || 'This person is lazy.,Nothing was written...'}</p>
      <p>Location:{user.location || 'Unknown'}</p>
    </div>
  )
}
▶ 試してみよう

出力:

TEXT
横並びの対比:HTML 構文と JSX 構文が同等のマークアップを示す。interface で props を型定義した TypeScript の React コンポーネント

▶ サンプル:種類の条件付きレンダリングの並列比較

JSX
// ============================================
// Example:Combined Use of Three Types of Conditional Rendering in the User Panel
// ============================================

function UserPanel({ user }) {
  return (
    <div className="panel">
      {/* 1. Ternary:Choose one of the two */}
      <h2>
        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}
      </h2>

      {/* 2. &&:Display only when met */}
      {user.isVIP && (
        <p>Membership Expiration:{user.vipExpireDate}</p>
      )}

      {/* 3. ||:Floor value */}
      <p>Signature:{user.slogan || 'This person is really cool.,No signature'}</p>

      {/* Combined Use:List of Tags */}
      {user.tags && user.tags.length > 0 && (
        <div>
          {user.tags.map(tag => (
            <span key={tag} style={{ background: '#f0f0f0', padding: '2px 8px', margin: '2px', borderRadius: '4px' }}>
              {tag}
            </span>
          ))}
        </div>
      )}
    </div>
  )
}
▶ 試してみよう

出力:

TEXT
VIP の場合:"⭐ Premium Member" + 会員有効期限。VIP でない場合:"Regular User"。シグネチャ:ユーザーのテキストまたはフォールバックコンテンツ。タグは存在する場合のみ表示。


4. リストのレンダリング:.map()

(1) 基本的な使い方

Array.map() 配列を順に処理し、各要素に対して JSX を返す:

▶ 試してみよう

(2) レンダリングオブジェクトの配列

▶ 試してみよう

(3) リスト表示のための3つのデータ変換手法

JSX
const items = [
  { id: 1, name: 'React Basics', price: 0 },
  { id: 2, name: 'Vue Getting Started', price: 0 },
  { id: 3, name: 'Node.js Real-World Experience', price: 99 }
]

// 1. Direct Rendering
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 2. Filter First, Then Render(Free Courses)
{items
  .filter(item => item.price === 0)
  .map(item => <li key={item.id}>{item.name}(Free)</li>)
}

// 3. Extract Rendering Functions
function renderItem(item) {
  return (
    <li key={item.id}>
      {item.name} - ${item.price}
    </li>
  )
}
{items.map(renderItem)}
▶ 試してみよう

5. 鍵の重要性

React は、リスト内の各要素を識別するために key を使用します。キーが変更されない場合、React はその DOM ノードを再利用します。キーが変更された場合、React はその DOM ノードを破棄して再作成します。

100%
graph LR
    A[List Changes] --> B{key Changes?}
    B -->|No, key The same| C[Reuse DOM Node<br/>Update only the changes]
    B -->|Yes, key It's changed| D[Delete Old Nodes<br/>Create a New Node]
    B -->|No, None key| E[Render All Over]

出力:

TEXT
サブタイトル:"        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}      "。表示:"0 && ("
主要戦略 実績 提言
データベースの一意ID ✅ 最適 ⭐⭐⭐
一意の文字列(UUID) ✅ 最高 ⭐⭐⭐
配列のインデックス ⚠️ リスクあり(リストが変更されると状態が不整合になる可能性がある) ⭐
乱数 ❌ 範囲(レンダリングごとに異なる) ❌
キーが渡されていない ❌ パフォーマンスが低い ❌

▶ サンプル:キーがある場合とない場合

▶ 試してみよう

出力:

TEXT
入力フィールドを持つ 2 つのリスト。"Randomize" をクリック:✅ リスト(key=id)は入力値とテキストの並びが維持される;❌ リスト(key=index)は入力値とテキストがずれる


6. 空の状態と読み込み中の状態

ステータス 状態 UIの動作 対応
読み込み中 loading === true スピナー/スケルトン画面 if (loading) return <Spinner />
読み込みに失敗しました error !== null エラーメッセージ + 再試行ボタン if (error) return <ErrorView />
空のデータ data.length === 0 空の状態のイラスト+コピー if (!data.length) return <Empty />
通常 上記のいずれでもない データ一覧を表示 デフォルト:通常のUIに戻る

実際のプロジェクトでは、データを読み込む際に処理しなければならない3つの状態があります:

JSX
// ============================================
// Example:Complete List Component(3 Handling Different States)
// ============================================

function ArticleList() {
  const [articles, setArticles] = React.useState([])
  const [isLoading, setIsLoading] = React.useState(true)
  const [error, setError] = React.useState(null)

  // Simulated Loading(Actually, it is API Call)
  React.useEffect(() => {
    fetch('/api/articles')
      .then(res => {
        if (!res.ok) throw new Error('Network request failed')
        return res.json()
      })
      .then(data => {
        setArticles(data)
        setIsLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setIsLoading(false)
      })
  }, [])

  // 1. Loading...
  if (isLoading) {
    return (
      <div style={{ textAlign: 'center', padding: '40px' }}>
        <div className="spinner" />
        <p>Loading article...</p>
      </div>
    )
  }

  // 2. Failed to load
  if (error) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#ff4d4f' }}>
        <p>❌ Failed to load:{error}</p>
        <button onClick={() => window.location.reload()}>Reload</button>
      </div>
    )
  }

  // 3. Empty data
  if (articles.length === 0) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#999' }}>
        <p>📭 No articles available</p>
        <p>No one has posted an article yet.,Go ahead and write your first one!!</p>
      </div>
    )
  }

  // 4. Normal Rendering
  return (
    <div>
      <h3>List of Articles(Total  {articles.length}  articles)</h3>
      {articles.map(article => (
        <ArticleCard key={article.id} article={article} />
      ))}
    </div>
  )
}
▶ 試してみよう

7. 完全な例:商品カテゴリと検索

▶ 実行例を展開(81 行)
▶ 試してみよう

出力:

TEXT
表示:"Randomize the order"。ボタン:Randomize the order。入力:Enter content、The content may be misaligned

期待される出力:検索フィルタリング、カテゴリフィルタリング、検索結果がない場合の通知、および統計機能を備えた、完全な商品一覧ページ。


▶ サンプル:フォームフィールドの動的レンダリング

JSX
function DynamicForm() {
  const [formType, setFormType] = useState('personal')
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    company: '',
    taxId: '',
    budget: '',
  })

  function handleChange(e) {
    const { name, value } = e.target
    setFormData(prev => ({ ...prev, [name]: value }))
  }

  const personalFields = (
    <>
      <input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
      <input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
    </>
  )

  const businessFields = (
    <>
      <input name="company" value={formData.company} onChange={handleChange} placeholder="Company" />
      <input name="taxId" value={formData.taxId} onChange={handleChange} placeholder="Tax ID" />
      <input name="budget" value={formData.budget} onChange={handleChange} placeholder="Budget ($)" />
    </>
  )

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <h3>Registration</h3>
      <div style={{ marginBottom: 12 }}>
        {['personal', 'business'].map(type => (
          <button
            key={type}
            onClick={() => setFormType(type)}
            style={{
              padding: '6px 16px', marginRight: 8, cursor: 'pointer',
              backgroundColor: formType === type ? '#1890ff' : '#f0f0f0',
              color: formType === type ? 'white' : '#333', border: 'none', borderRadius: 4,
            }}
          >
            {type === 'personal' ? 'Personal' : 'Business'}
          </button>
        ))}
      </div>
      {formType === 'personal' ? personalFields : businessFields}
      <button style={{ marginTop: 12, padding: '8px 24px', cursor: 'pointer' }}>Submit</button>
    </div>
  )
}
▶ 試してみよう

出力:

TEXT
個人フォーム:Name + Email フィールド。企業フォーム:Company + Tax ID + Budget フィールド。ボタンで切り替え。末尾に送信ボタン。

出力:

TEXT
検索入力フィールドで項目をフィルタリング。並べ替えボタン:A-Z(名前順)/ Stars(評価順)。現在の並べ替えは青色でハイライト。フィルタ + 並べ替え後のリストがリアクティブに更新される

▶ サンプル:リストの検索、フィルタリング、および並べ替え

▶ 試してみよう

出力:

TEXT
フレームワーク:React ⭐220k、Vue ⭐207k、Tailwind ⭐80k、Django ⭐78k、Express ⭐63k。検索フィルタ;A-Z/Stars の並べ替えを切り替え。

出力:

TEXT
表示 "{cat.name}"

▶ サンプル:ネストされたリストのレンダリング—カテゴリ別商品の表示

▶ 試してみよう

出力:

TEXT
Electronics: Keyboard $79、Monitor $399
Furniture: Desk $249、Chair $189
Books: "No products yet"(斜体の空状態)

❓ よくある質問

Q .map() はネストできますか?複数の .map() 呼び出しを使用する場合、キーはどのように扱われますか?
A はい、ネストできます。.map() の各レベルには、それぞれ独自のキーが必要です。例えば、「カテゴリー → 商品: cats.map(c => <div key={c.id}><h3>{c.name}</h3>{c.products.map(p => <span key={p.id}>{p.name}</span>)}</div>)」をレンダリングする場合などです。異なるレベルのキーは同じでも構いません(それぞれの配列内で一意である限り)。
Q 三項演算子と && 構文、どちらが優れていますか?
A それぞれに適した用途があります。&& は「条件が満たされた場合は表示し、満たされない場合は何も表示しない」場合に適しています。三項演算子 ? : は「条件 A が満たされた場合はコンポーネント X を表示し、条件 B が満たされた場合はコンポーネント Y を表示する」場合に適しています。三項演算子を使って「条件が満たされない場合は何も表示しない」ようにしたい場合、構文は condition ? <Comp /> : null になりますが、この場合は && の方が簡潔です。ただし、&& には「0 レンダリングの落とし穴」があることに注意してください。つまり、0 && <Comp /> は 0 をレンダリングしてしまいます。

📖 まとめ


📝 練習問題

  1. 基本問題(難易度 ⭐):tempのプロップスに基づいて、気温が35°Cより高い場合は赤色の🔥を、15~35°Cの場合は黄色の☀️を、15°Cより低い場合は青色の❄️を表示するTemperatureDisplayコンポーネントを作成してください。
  2. 上級問題(難易度 ⭐⭐):2つのプロップ(todos(タスクの配列)とfilter(all/completed/uncompleted))を受け取るTodoFilterコンポーネントを作成し、状態に基づいたフィルタリングを実装してください。
  3. 課題(難易度:⭐⭐⭐):Pagination、totalPages、currentPage、onPageChange のプロパティを受け取り、ページ番号ボタンをレンダリングする Pagination コンポーネントを作成してください。このコンポーネントは、最初と最後のページ、および省略記号に対応している必要があります。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%