JSX で記事リストをレンダリング

この章では、JSX 式を使って JS データを UI にレンダリングし、ブログのホームページに記事カードのリストを表示できるようになります。


{} で JSX に式を埋め込む

JSX では、波括弧 {}は JavaScript の世界への入り口です。

任意の JS 式を波括弧の中に置けます:変数、演算、関数呼び出し、三項演算式などです。

実例

function HomePage() {
  const blogTitle = 'サンプル 前端笔记'
  const author = '小明'

  return (
    <div>
      <h1>{blogTitle}</h1>
      <p>著者:{author}</p>
      <p>現在時刻:{new Date().toLocaleDateString()}</p>
      <p>ようこそメッセージ:{author ?`你好,${author}` : '欢迎访客'}</p>
    </div>
  )
}

ページには以下のように表示されます:

サンプル 前端笔记
作者:小明
当前时间:2024/5/15
欢迎语:你好,小明

波括弧の中に置けるのは式(値を計算できるコード)であり、文は置けません。例えば{if (condition) ...}不正ですが、{condition ? 'A' : 'B'}合法です。


属性バインディング

HTML 属性は JSX では波括弧を使って動的にバインドする必要があります。

実例

function ArticleCard() {
  const coverUrl = '/images/react-cover.png'
  const linkUrl = '/post/1'

  return (
    <div>
      {/* 动态属性:花括号绑定 JS 变量 */}
      <img src={coverUrl} alt=記事カバー />
      <a href={linkUrl}>続きを読む</a>

      {/* 注意:class 要写成 className */}
      <div className="card active">カードの内容</div>

      {/* style はオブジェクトを受け取り、プロパティ名はキャメルケースです */}
      <div style={{ color: '#427DB8', fontSize: '16px' }}>
緑色の文字
      </div>
    </div>
  )
}
HTMLJSX説明
class="btn"className="btn"class は JS の予約語です
style="color: red"style={{ color: 'red' }}オブジェクトを受け取り、プロパティ名はキャメルケース
src="xxx.html"src={variable}動的な値は波括弧を使う
onclick="fn()"onClick={fn}イベント名はキャメルケース、関数の参照を渡す

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

React には Vue のようにv-forそのようなディレクティブはありません。

JSX でリストをレンダリングするには、JavaScript のarray.map()メソッドを使います。

実例

function ArticleList() {
  // JS オブジェクト配列で記事データをシミュレートする
  const articles = [
    {
      id: 1,
      title: 'React 入门完全指南',
      summary: 'ゼロから React Hooks を学び、useState、useEffect などの核心概念をカバーします。',
      date: '2024-05-10',
      category: 'React',
      cover: '/images/react.png'
    },
    {
      id: 2,
      title: 'JavaScript 异步编程详解',
      summary: '一文で Promise、async/await、イベントループとマイクロタスクキューを理解する。',
      date: '2024-05-08',
      category: 'JavaScript',
      cover: '/images/js.png'
    },
    {
      id: 3,
      title: 'CSS Grid 布局实战',
      summary: 'CSS Grid を使用して、複雑なレスポンシブレイアウトを簡単に実現します。',
      date: '2024-05-05',
      category: 'CSS',
      cover: '/images/css.png'
    }
  ]

  return (
    <div className="article-list">
      {/* map 遍历数组,每条数据返回一段 JSX */}
      {articles.map(article => (
        <div key={article.id} className="card">
          <img src={article.cover} alt={article.title} />
          <div className="card-body">
            <span className="category">{article.category}</span>
            <h3>{article.title}</h3>
            <p>{article.summary}</p>
            <span className="date">{article.date}</span>
          </div>
        </div>
      ))}
    </div>
  )
}

key 属性の重要性

keyこれはリスト内の各要素に割り当てられる一意の識別子で、React がどの要素が変更されたかを認識するのに役立ちます。

key がない場合、React は「インプレース更新」戦略を採用するため、状態が混乱したりパフォーマンスが低下したりする可能性があります。

key の値結果
一意の id(推奨)各要素を正しく追跡し、効率的な Diff
配列のインデックス index挿入/削除時に入力欄の内容が乱れる可能性があります
key を書かないコンソール警告、パフォーマンス低下

常に map が返す JSX に一意で安定したkey を付けましょう。特にリスト項目が追加・削除・並べ替えされる可能性がある場合は、index を key に使わないでください。


条件レンダリング

React には v-if はありませんが、条件レンダリングを実現する方法が3つあります。

実例

function ArticlePage() {
  const articles = []
  const isLoading = false

  return (
    <div>
      {/* 方式一:三元表达式 — 适合二选一 */}
      {isLoading ? (
        <p>読み込み中、お待ちください...</p>
      ) : (
        <p>読み込み完了!</p>
      )}

      {/* 方式二:&& 短路 — 适合「有条件就显示,没条件就不显示」 */}
      {articles.length === 0 && <p>まだ記事がありません。お楽しみに。</p>}

      {/* 方式3:if/else をコンポーネントのトップレベルで — 複雑な多条件ロジックに適している */}
      {articles.length > 0 ? (
        <div>
          {articles.map(a => <div key={a.id}>{a.title}</div>)}
        </div>
      ) : (
        <p>データがありません</p>
      )}
    </div>
  )
}

3つの条件レンダリング方法の比較

方式書き方適用シーン
三項演算子? :{cond ? <A /> : <B />}二者択一で、必ず何かを返す必要がある
短絡&&{cond && <A />}あれば表示し、なければ表示しない
事前の if 判定return の前に変数に JSX を格納しておく条件分岐が複雑な場合

&&短絡には落とし穴があります:条件が数値 0 の場合、"0" がレンダリングされます。推奨は&&左側は常にブール値にすること:{list.length > 0 && ...}ではなく{list.length && ...}。


実践:完全版ブログホームページ

実例

// 文件路径:src/App.jsx
import './App.css'

function App() {
  const articles = [
    {
      id: 1, title: 'React 入门完全指南',
      summary: 'ゼロから React Hooks を学び、useState、useEffect などの核心概念をカバーします。',
      date: '2024-05-10', category: 'React', cover: '/images/react.png'
    },
    {
      id: 2, title: 'JavaScript 异步编程详解',
      summary: '一文で Promise、async/await、イベントループとマイクロタスクキューを理解する。',
      date: '2024-05-08', category: 'JavaScript', cover: '/images/js.png'
    },
    {
      id: 3, title: 'CSS Grid 布局实战',
      summary: 'CSS Grid を使用して、複雑なレスポンシブレイアウトを簡単に実現します。',
      date: '2024-05-05', category: 'CSS', cover: '/images/css.png'
    }
  ]

  return (
    <div className="app">
      <header className="navbar">
        <h1 className="logo">サンプル Blog</h1>
        <nav>
          <a href="/">ホーム</a>
          <a href="#">このサイトについて</a>
        </nav>
      </header>

      <main className="container">
        <h2 className="section-title">最新記事</h2>

        {/* 条件渲染:有文章就显示,没文章就显示空态 */}
        {articles.length === 0 ? (
          <p className="empty-tip">まだ記事がありません。お楽しみに。</p>
        ) : (
          <div className="article-grid">
            {/* 列表渲染:map 遍历数组 */}
            {articles.map(article => (
              <div key={article.id} className="article-card">
                <img src={article.cover} alt={article.title} className="card-cover" />
                <div className="card-content">
                  <span className="card-category">{article.category}</span>
                  <h3 className="card-title">{article.title}</h3>
                  <p className="card-summary">{article.summary}</p>
                  <span className="card-date">{article.date}</span>
                </div>
              </div>
            ))}
          </div>
        )}
      </main>

      <footer className="footer">
        <p>© 2024 サンプル Blog. Powered by React.</p>
      </footer>
    </div>
  )
}

export default App

実例

/* 文件路径:src/App.css 追加以下样式 */
.section-title {
  font-size: 24px;
  margin-bottom: 20px;
  color: #333;
}

.article-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 24px;
}

.article-card {
  background: #fff;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(0,0,0,0.08);
  transition: transform 0.2s, box-shadow 0.2s;
  cursor: pointer;
}

.article-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}

.card-cover {
  width: 100%;
  height: 180px;
  object-fit: cover;
}

.card-content {
  padding: 16px;
}

.card-category {
  display: inline-block;
  padding: 2px 10px;
  background: #e3f2fd;
  color: #1976d2;
  border-radius: 12px;
  font-size: 12px;
  margin-bottom: 8px;
}

.card-title {
  font-size: 18px;
  margin-bottom: 8px;
  color: #222;
}

.card-summary {
  font-size: 14px;
  color: #666;
  line-height: 1.6;
  margin-bottom: 12px;
}

.card-date {
  font-size: 12px;
  color: #999;
}

.empty-tip {
  text-align: center;
  color: #999;
  padding: 60px 0;
  font-size: 16px;
}

本章のまとめ

この章では、JSX の4つの基本的な操作を学びました:{}式の埋め込み、属性バインディング(className / style)、map()リストレンダリング + key、条件レンダリング(三項演算子 / 短絡)。

これで、ブログのホームページは JS データに基づいて記事カードを動的にレンダリングできるようになりました。

その他の拡張