今回やること
前回は、JavaScriptのitemオブジェクトを使って、
一覧画面に1件分のデータを表示しました。
しかし、実際の一覧では1件だけではなく、 複数の「気になるもの」を表示したいですよね。
今回は、複数のデータを配列にまとめ、
map()を使ってテーブルへ一覧表示します。
あわせて、Reactで一覧を表示するときに必要になる
keyについても確認します。
公開日:更新日:
前回は、JavaScriptのitemオブジェクトを使って、
一覧画面に1件分のデータを表示しました。
しかし、実際の一覧では1件だけではなく、 複数の「気になるもの」を表示したいですよね。
今回は、複数のデータを配列にまとめ、
map()を使ってテーブルへ一覧表示します。
あわせて、Reactで一覧を表示するときに必要になる
keyについても確認します。
前回は、1つのitemオブジェクトを用意しました。
const item = {
id: "sample-1",
name: "はじめてのWebデザイン本",
category: "本",
status: "気になる",
price: 2200,
memo: "図が多くて読みやすそう",
};
今回は複数のデータを一覧表示したいので、 複数のオブジェクトをまとめて管理できる配列を使います。
itemをitemsに変更し、
[]の中に2つのオブジェクトを用意しましょう。
const items = [
{
id: "sample-1",
name: "はじめてのWebデザイン本",
category: "本",
status: "気になる",
price: 2200,
memo: "図が多くて読みやすそう",
},
{
id: "sample-2",
name: "小さなサイドテーブル",
category: "家具",
status: "欲しい",
price: 3500,
memo: "部屋に置きやすそう",
},
];
itemsは配列で、その中に2つのオブジェクトが入っています。
items 配列
↓
[
{ 1件目のデータ },
{ 2件目のデータ }
]
[]は配列を作るための記号、
その中にある{}は1件分のオブジェクトを作るための記号です。
前回は、1件分のデータをh3やpで表示しました。
今回は複数件を一覧として並べるため、
HTMLのtableを使います。
まずは、items配列の1件目をテーブルに表示してみましょう。
配列では、先頭の要素を0として数えるため、1件目はitems[0]で取り出します。
たとえば、1件目の名前を取り出す場合は次のように書きます。
items[0].name
items[0]で配列の1件目のオブジェクトを取り出し、
.nameでその中のnameプロパティを取り出しています。
前回の一覧画面にあったh3やpの部分を、
次のテーブルに置き換えましょう。
<table>
<thead>
<tr>
<th>名前</th>
<th>カテゴリ</th>
<th>ステータス</th>
<th>価格</th>
<th>メモ</th>
</tr>
</thead>
<tbody>
<tr>
<td>{items[0].name}</td>
<td>{items[0].category}</td>
<td>{items[0].status}</td>
<td>{items[0].price}円</td>
<td>{items[0].memo}</td>
</tr>
</tbody>
</table>
では、2件目も表示するとしたらどうなるでしょうか。
2件目はitems[1]で取り出せるので、もう1つtrを書けば表示できます。
<tr>
<td>{items[1].name}</td>
<td>{items[1].category}</td>
<td>{items[1].status}</td>
<td>{items[1].price}円</td>
<td>{items[1].memo}</td>
</tr>
2件ならこの方法でも書けますが、データが10件、20件と増えるたびに、同じ形のtrを追加するのは大変です。
そこで、配列のデータを1件ずつ処理できるmap()を使います。
map()はJavaScriptの配列メソッドです。
配列の要素を1件ずつ処理して、その結果から新しい配列を作れます。
今回は、この仕組みを使ってデータごとにtrを作ります。
まずは、JSXを使わないシンプルな形で見てみましょう。
items.map((item) => {
// itemを使った処理
});
このコードでは、items配列の中から
データを1件ずつ取り出し、
そのとき処理している1件をitemとして扱います。
items
↓
配列全体
map()
↓
配列の要素を1件ずつ処理する
item
↓
そのとき処理している1件分のオブジェクト
1回目の処理では、itemは
「はじめてのWebデザイン本」のオブジェクトになります。
item.name
// "はじめてのWebデザイン本"
2回目の処理では、itemは
「小さなサイドテーブル」のオブジェクトになります。
item.name
// "小さなサイドテーブル"
つまり、items[0]やitems[1]を
自分で書き分けなくても、
map()が配列のデータを順番に処理してくれます。
では、先ほど数値を指定していたtrの部分を、map()で繰り返して作ってみましょう。
tbodyの中を、次のように書き換えます。
<tbody>
{items.map((item) => (
<tr>
<td>{item.name}</td>
<td>{item.category}</td>
<td>{item.status}</td>
<td>{item.price}円</td>
<td>{item.memo}</td>
</tr>
))}
</tbody>
items.map(...)はJavaScriptの式なので、JSXの中で使うために外側を{}で囲んでいます。
map()の中では、そのとき処理している1件分のitemを使って、trを1行作っています。
itemsには2件のデータが入っているため、このtrも2回作られます。
1件目
item.name → "はじめてのWebデザイン本"
↓
1行目のtrを作る
2件目
item.name → "小さなサイドテーブル"
↓
2行目のtrを作る
これで、データが増えても同じtrを手書きで追加する必要がなくなります。
map()を使って、2件のデータを一覧表示できました。
ただし、この時点ではまだ1つ問題があります。
ブラウザの開発者ツールでConsoleを確認すると、
keyに関する警告が表示されています。
画面には問題なく2件表示されていますが、
Reactでmap()を使って一覧を表示するときは、
繰り返して作る各要素にkeyを指定します。
keyは、Reactが一覧の各要素を
識別するために使う値です。
今回は、それぞれのデータにidが用意されています。
このidを使って、trにkeyを追加しましょう。
先ほどのtrを、次のように変更します。
<tr key={item.id}>
1件目ではsample-1、
2件目ではsample-2がkeyとして使われます。
ファイルを保存すると、先ほど表示されていた
keyに関する警告は表示されなくなります。
keyには、
同じ一覧の中で重複せず、データを安定して識別できる値を使うのが基本です。
今回登場した内容を、ReactとJavaScriptに分けて整理します。
| 種類 | 今回登場したもの |
|---|---|
| React | key、JSXの{}を使った一覧表示 |
| JavaScript | 配列、オブジェクト、map() |
items配列を作ったり、
map()で1件ずつ処理したりする部分はJavaScriptです。
そのJavaScriptの処理によって作ったJSXをReactで表示し、
各要素を識別するためにkeyを指定しています。
items配列にまとめた[]は配列、{}は1件分のオブジェクトを作るために使うitems[0]で配列の1件目を取り出せるmap()で配列のデータを1件ずつ処理できるmap()を使って同じtrを件数分作ったkeyを指定するidがある場合は、安定したidをkeyに使う
今回のApp.jsxの完成コードです。
前回までの画面切り替えを残したまま、
items配列をmap()でテーブル表示します。
import { useState } from "react";
export default function App() {
const items = [
{
id: "sample-1",
name: "はじめてのWebデザイン本",
category: "本",
status: "気になる",
price: 2200,
memo: "図が多くて読みやすそう",
},
{
id: "sample-2",
name: "小さなサイドテーブル",
category: "家具",
status: "欲しい",
price: 3500,
memo: "部屋に置きやすそう",
},
];
const [screen, setScreen] = useState("list");
if (screen === "create") {
return (
<main className="form-screen">
<header className="screen-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" className="secondary" onClick={() => setScreen("list")}>
一覧へ
</button>
</div>
</header>
<h2 className="screen-title">新規登録画面</h2>
</main>
);
}
if (screen === "edit") {
return (
<main className="form-screen">
<header className="screen-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" className="secondary" onClick={() => setScreen("list")}>
一覧へ
</button>
</div>
</header>
<h2 className="screen-title edit-title">編集画面</h2>
</main>
);
}
return (
<main className="list-screen">
<header className="list-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" onClick={() => setScreen("create")}>
新規登録
</button>
<button type="button" onClick={() => setScreen("edit")}>
編集
</button>
</div>
</header>
<h2 className="screen-title">一覧画面</h2>
<table>
<thead>
<tr>
<th>名前</th>
<th>カテゴリ</th>
<th>ステータス</th>
<th>価格</th>
<th>メモ</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.category}</td>
<td>{item.status}</td>
<td>{item.price}円</td>
<td>{item.memo}</td>
</tr>
))}
</tbody>
</table>
</main>
);
}