React・データを流しこむ

配列から一覧を作る(mapとkey)

8羽ぶんのカードを手で書く必要はありません。データを配列にまとめ、mapでカードに変換します。あわせて、Reactが必ず要求してくるkeyが何のためにあるのか、indexを使うと何が起きるのかも確かめます。

いま3枚のカードを手で書いています。8羽にするなら8回コピー——さすがにつらいです。データを配列にして、そこから自動で並べます。

データをJavaScriptのファイルに置く

まずは表示から切り離して、データだけをまとめます。

export const birds = [
  {
    id: 1,
    name: 'シマエナガ',
    emoji: '🐦',
    area: '北海道',
    rare: 3,
    memo: '雪のようにまっ白な小鳥。ふわふわの丸い体。',
  },
  {
    id: 2,
    name: 'スズメ',
    emoji: '🐤',
    area: 'まちなか',
    rare: 1,
    memo: 'いちばん身近な小鳥。ちゅんちゅん鳴いている。',
  },
  // …このあと8羽ぶん続く
]

export defaultではなく名前つきのexportにしました。データのファイルには複数のまとまりを置くこともあるためです。読みこむ側は波かっこが要ります。

import { birds } from './data/birds.js'

mapでカードに変える

配列を一覧にするのは、この4行です。

<div className="mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
  {birds.map((bird) => (
    <BirdCard key={bird.id} name={bird.name} emoji={bird.emoji} area={bird.area} memo={bird.memo} />
  ))}
</div>

やっていることは、前回JavaScriptの回で見たmapそのものです。

// 文字列の配列 → 文字列の配列
names.map((name) => `🐦 ${name}`)

// オブジェクトの配列 → JSXの配列
birds.map((bird) => <BirdCard />)

変換後の中身がJSXになっただけで、mapの使い方は同じです。JSXの配列を波かっこの中に置くと、Reactが順番に並べて表示します。

keyは何のためにあるのか

mapでJSXを作ると、Reactが必ずこう言ってきます。

Warning: Each child in a list should have a unique "key" prop.

keyは、Reactが「どの要素がどのデータのものか」を見分けるための目印です。

Reactは画面を作り直すとき、前回のJSXと今回のJSXを見比べて、変わったところだけを書きかえます。並び順が変わったとき、keyが無いと「1番目と2番目が入れかわった」のか「1番目と2番目の中身が両方書きかわった」のかを区別できません。

keyがあれば、中身ではなく目印で追いかけられるので、要素をそのまま動かすだけで済みます。

<BirdCard key={bird.id} />

件数もデータから出す

「全8羽」と手で書いていた部分も、配列から出せます。

<p className="mt-1 text-sm text-slate-500">全{birds.length}羽をあつめよう</p>

データを1羽足すだけで、カードも件数も同時に増えます。冒頭の回で見た「データと画面がズレる」問題が、ここで消えました。

並べ方はTailwindのグリッドで

カードが8枚になると、縦一列では長すぎます。画面幅に応じて列を増やします。

<div className="mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">

スマホは1列、sm以上で2列、lg以上で3列。HTMLに書くときとまったく同じクラスです。Reactだからといって特別な書き方は要りません。

Tailwind CSS画面幅で切りかえる(md:とlg:)sm:lg:の考え方はTailwindコースのこの回に。Reactの中でも規則は同じです

公式ドキュメントも見てみる

ja.react.dev外部サイトリストのレンダー – React公式の「リストのレンダー」。「配列の項目に順序を保つためのkeyを与える」の節に、keyの決まりとindexを避ける理由がまとまっています

このレッスンのまとめ

  1. データは配列にまとめ、export constで書き出す(受け取りは波かっこつき)
  2. 一覧はmapでJSXの配列に変換して、波かっこの中に置く
  3. forEachreturn忘れは画面が真っ白になる定番の原因
  4. keyはReactが要素を見分けるための目印。データのidを使う
  5. indexをkeyにすると、並べかえたときに状態が別の行へ移る
  6. 件数もbirds.lengthから出せば、データと表示がズレなくなる

次は、条件によって出し分ける方法です。レアな鳥にだけバッジを付けて、説明がまだ無い鳥には別の文を出します。

やってみよう:8羽ぶんを配列から並べる

データのファイルを作って、一覧を自動で組み立てます。

  1. src/data/birds.jsを作り、8羽ぶんの配列を書く(下のコード)
  2. src/components/BirdCard.jsxはそのままでOK
  3. src/App.jsxbirdsを読みこみ、3つの<BirdCard />mapに置きかえる
  4. 件数の「8」も{birds.length}に差しかえる
export const birds = [
  { id: 1, name: 'シマエナガ', emoji: '🐦', area: '北海道', rare: 3, memo: '雪のようにまっ白な小鳥。ふわふわの丸い体。' },
  { id: 2, name: 'スズメ', emoji: '🐤', area: 'まちなか', rare: 1, memo: 'いちばん身近な小鳥。ちゅんちゅん鳴いている。' },
  { id: 3, name: 'カルガモ', emoji: '🦆', area: 'かわ', rare: 1, memo: '親子で列になって歩く姿が有名。' },
  { id: 4, name: 'フクロウ', emoji: '🦉', area: 'もり', rare: 2, memo: '夜に活動する。首がぐるりとよく回る。' },
  { id: 5, name: 'ハト', emoji: '🕊️', area: 'こうえん', rare: 1, memo: '地面を歩きながらえさをさがす。' },
  { id: 6, name: 'トビ', emoji: '🦅', area: 'うみべ', rare: 2, memo: '空高く輪をえがいて飛ぶ。ピーヒョロロと鳴く。' },
  { id: 7, name: 'ハシブトガラス', emoji: '🐦‍⬛', area: 'まちなか', rare: 1, memo: '' },
  { id: 8, name: 'ハクチョウ', emoji: '🦢', area: 'みずうみ', rare: 3, memo: '冬にやってくる大きな白い鳥。' },
]
import BirdCard from './components/BirdCard.jsx'
import { birds } from './data/birds.js'

function App() {
  return (
    <div className="min-h-screen bg-sky-50 p-8">
      <h1 className="text-2xl font-bold text-sky-800">しまちゃんのことりずかん</h1>
      <p className="mt-1 text-sm text-slate-500">全{birds.length}羽をあつめよう</p>

      <div className="mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
        {birds.map((bird) => (
          <BirdCard
            key={bird.id}
            name={bird.name}
            emoji={bird.emoji}
            area={bird.area}
            memo={bird.memo}
          />
        ))}
      </div>
    </div>
  )
}

export default App

この時点のフォルダはこうなります。

shima-zukan/
├── node_modules/
├── public/
├── src/
│   ├── components/
│   │   └── BirdCard.jsx
│   ├── data/
│   │   └── birds.js       ← 今回作る
│   ├── App.jsx
│   ├── index.css
│   └── main.jsx
├── index.html
├── package.json
└── vite.config.js
水色の背景に8羽ぶんのカードが3列のグリッドで並んでいる。それぞれ絵文字・名前・すんでいる場所のチップ・説明が表示されている
8羽が3列に並びました。ハシブトガラスの説明だけ空っぽですが、これは次回わざと使います

ブラウザの開発者ツールでコンソールを開き、keyの警告が出ていないことも確認しておいてください。

よくある質問

Reactで配列を表示するにはどうすればいいですか?
配列のmapを使って、1件ずつJSXに変換します。mapは新しい配列を返すので、その配列をそのまま波かっこの中に置けば、要素が順番に並びます。
keyには何を指定すればいいですか?
そのデータを一意に識別できる値です。多くの場合はデータが持つidを使います。名前のように重複しうる値や、順番が変わるindexは避けます。
keyを付けないとどうなりますか?
画面には表示されますが、コンソールに警告が出ます。並べかえや削除をしたときに、入力中の文字やチェック状態が別の行に移ってしまうといった不具合が起きます。
mapの中でindexをkeyに使ってはいけませんか?
並べかえ・追加・削除が起きない一覧なら実害はありません。ただし順番が変わる可能性のある一覧では、indexは同じ行を指し続けないため不具合の原因になります。