本文へスキップ
ウェブエンジニア問題集
リスト

Reactで配列をリスト表示する — map・filter・keyの使い方

約6分
この章の目次開く

商品一覧やコメント一覧のように、同じ見た目をデータの数だけ繰り返したい場面では、JSXを1つずつ手書きしません。データを配列にまとめ、JavaScriptの map() で各要素をJSXへ変換します。

このページでは、配列から必要なデータを選び、それぞれをReact要素へ変換し、Reactが正しく追跡できるように key を付けるまでの流れを整理します。

配列からリストをレンダーする

まずは、タスクの配列から <li> を作る基本形を見てみましょう。

const tasks = [
  { id: 'task-1', title: 'メールを確認する' },
  { id: 'task-2', title: '資料を作成する' },
  { id: 'task-3', title: 'レビューを依頼する' },
];
 
function TaskList() {
  return (
    <ul>
      {tasks.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}
tsx

tasks.map(...) は、配列の各データを <li> というReact要素へ変換します。変換後はReact要素の配列になり、{} でJSXに埋め込むとReactが順番にレンダーします。

map() でデータをJSXへ変換する

構文: array.map(callbackFn)

引数渡せるもの説明
callbackFn関数配列の各要素に対して実行し、変換後の値を返す関数

callbackFn は次の3つの引数を受け取れます。通常は第1引数だけで十分です。

コールバックの引数説明
element現在処理している配列要素
index現在の要素の位置(0から始まる)
arraymap() を呼び出した元の配列

戻り値: コールバックの戻り値を並べた新しい配列。元の配列は変更しません。

JSXの中へ直接書く方法と、一度変数へ入れる方法は同じ結果になります。

// JSXの中で変換する
return <ul>{tasks.map((task) => <li key={task.id}>{task.title}</li>)}</ul>;
 
// 先にReact要素の配列を作る
const taskItems = tasks.map((task) => (
  <li key={task.id}>{task.title}</li>
));
 
return <ul>{taskItems}</ul>;
tsx

filter() で表示するデータを絞り込む

完了していないタスクだけを表示するなど、条件に合う項目だけが必要なら、先に filter() で絞り込んでから map() でJSXへ変換します。

構文: array.filter(callbackFn)

引数渡せるもの説明
callbackFn関数各要素を残すなら true、除外するなら false を返す関数。受け取れる引数は map() と同じ

戻り値: 条件を通過した要素だけを含む新しい配列。元の配列は変更しません。

const tasks = [
  { id: 'task-1', title: 'メールを確認する', done: true },
  { id: 'task-2', title: '資料を作成する', done: false },
  { id: 'task-3', title: 'レビューを依頼する', done: false },
];
 
function RemainingTasks() {
  const remainingTasks = tasks.filter((task) => !task.done);
 
  return (
    <ul>
      {remainingTasks.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}
tsx

filter() はデータを選び、map() は選んだデータをJSXへ変換します。配列メソッド自体の詳しい使い方は「配列と主要メソッド — map・filter・reduce・find・some・every」でも解説しています。

key は要素の位置ではなく「正体」を伝える

map() でJSXを並べるときは、同じ配列内の各要素に key を指定します。

tasks.map((task) => (
  <li key={task.id}>{task.title}</li>
));
tsx

Reactは前回と今回のリストを比較し、どの要素が追加・削除・並べ替えされたのかを判断します。そのとき、要素を追跡するための目印になるのが key です。

適切な key は、兄弟要素の中で一意であり、同じデータにはレンダーをまたいで同じ値が割り当てられる、安定したIDです。
指定方法評価理由
key={task.id}推奨データと結び付いたIDなので、並べ替えても同じ要素を追跡できる
key={index}原則避ける追加・削除・並べ替えで同じデータの位置が変わる
key={Math.random()}使用しないレンダーのたびに値が変わり、毎回別の要素として作り直される

インデックスを key にすると、先頭へ項目を追加しただけで、それまで0番だった要素が1番へずれます。Reactが別のデータを同じ要素だと判断し、入力欄の値やコンポーネントのstateが意図しない項目へ移ることがあります。項目が固定され、追加・削除・並べ替えが絶対に起きない一覧を除き、データが持つIDを使いましょう。

データにIDがない場合は、レンダー中に作るのではなく、データを作成する時点でIDも保存します。データベースの主キーや、項目の作成時に発行したUUIDなどが使えます。

key を付ける場所

key は、map() が直接返す最も外側の要素に付けます。子コンポーネントの中にある <li> へ移しても、配列側の要素を識別できません。

function Task({ title }: { title: string }) {
  return <li>{title}</li>;
}
 
// keyはTaskに付ける
tasks.map((task) => (
  <Task key={task.id} title={task.title} />
));
tsx

1件につき複数の要素を返し、余分なDOM要素を増やしたくない場合は、短縮記法の <>...</> ではなく Fragment を使います。短縮記法には key を指定できないためです。

import { Fragment } from 'react';
 
tasks.map((task) => (
  <Fragment key={task.id}>
    <dt>{task.title}</dt>
    <dd>{task.done ? '完了' : '未完了'}</dd>
  </Fragment>
));
tsx

ちゃんと使うためのポイント

  • 表示するデータは配列にまとめ、map() でJSXへ変換する
  • 条件に合う項目だけ表示するときは、filter() のあとに map() を使う
  • key には兄弟間で一意かつ、レンダーをまたいで変わらないIDを使う
  • 配列のインデックスや、レンダー中に作った乱数を key にしない
  • key は map() が直接返す最も外側の要素に付ける

参考リンク


Reactクイズに挑戦するmap・filterによるリスト表示とkeyの役割を4択クイズで確認しよう

次の章では、「Reactのレンダー・マウント・再レンダー・アンマウントの違い」を整理します。