HOWTO · React

Reactでforループを使う方法

Reactで`.map()`またはJavaScriptの`for`ループを使ってデータのリストを描画し、安定したkeyとコンポーネントのpropsを扱う方法を紹介します。

このページの内容

商品、ユーザーなどのレコードを含む配列がある場合、Reactでは各項目に対応する要素やコンポーネントを描画できます。通常の1対1の変換には.map()を使い、処理の手順を明示したほうが分かりやすい場合はJavaScriptのforループを使います。どちらの方法でも、Reactが描画できる要素の配列を作れます。

Reactで.map()を使ってリストを描画する

まず、表示するレコードを用意します。次の単独のJavaScript例では、各商品をラベルに変換しています。Reactもコンポーネントのマウントも使用していません。下の出力は、このJavaScript例で実際に確認されたものです。

const products = [
  { id: "mug", name: "Ceramic mug", price: 12 },
  { id: "notebook", name: "Notebook", price: 8 },
];

const labels = products.map((product) => `${product.name}: $${product.price}`);
console.log(labels.join("\n"));
Ceramic mug: $12
Notebook: $8

JSXでは、同じデータを要素の配列に変換します。たとえば、products.map((product) => <li key={product.id}>{product.name}: ${product.price}</li>)のように書けます。返された配列は、<ul>{products.map((product) => <li key={product.id}>{product.name}</li>)}</ul>のように、リストを置く場所のマークアップに含めます。Reactは配列をそのまま描画するため、ページ構造上必要な場合にだけラッパーを追加します。これらのJSX例はReactの描画を説明するもので、上記のJavaScript実行証拠の対象ではありません。

各レコードを再利用可能なコンポーネントに変換することもできます。function ProductCard(props) { return <li>{props.name}: ${props.price}</li>; }と定義し、products.map((product) => <ProductCard key={product.id} name={product.name} price={product.price} />)で描画します。子コンポーネントはnameとpriceをpropsオブジェクトで受け取り、その値を使って出力を組み立てます。代わりにレコード全体をproduct={product}として渡し、コンポーネント内でprops.product.nameとprops.product.priceを参照してもかまいません。コンポーネントに必要なデータが分かりやすくなるインターフェースを選びましょう。

.map()で返す同じ階層の各要素には、その兄弟要素の中で一意であり、同じレコードに対して変わらないkeyを指定します。通常はデータに含まれるIDを使います。項目の追加、削除、並べ替えが起こり得るリストでは、配列のインデックスを使わないでください。変更後にインデックスが別のレコードを指すと、Reactがコンポーネントの状態を誤った項目に対応付けることがあります。Reactは更新時にリスト要素を照合するためにkeyを使いますが、keyは子コンポーネントのpropsとして渡されません。キーとリスト描画の詳細はReactのリスト描画ガイドを参照してください。

JavaScriptのforループを使う

手順を明示しながら要素を組み立てたい場合は、JavaScriptのforループが役立ちます。コンポーネントでは、returnより前に配列を初期化し、各商品に対応するkey付きの要素を追加してから、返すマークアップにその配列を含めます。以下は、その流れを行ごとに示すコンポーネントです。上で宣言したproductsデータを使います。

function ProductList() {
const items = [];
for (let i = 0; i < products.length; i += 1) {
const product = products[i];
items.push(<li key={product.id}>{product.name}: ${product.price}</li>);
}
return <ul>{items}</ul>;
}

このコンポーネントはReact/JSXの例であり、実行済みのJavaScript例には含まれません。ループはコンポーネントが結果を準備する間に実行されます。文をJSXの中に直接置くことはできません。単純な1対1の変換なら、.map()のほうが短く、各レコードと要素の対応も把握しやすくなります。複数の文や条件分岐を使って結果を作る場合は、ループのほうが分かりやすいことがあります。どちらの方法でも、返す要素には安定したキーを指定してください。

即時実行関数を使う

即時実行関数式(IIFE)を使うと、JSX式の中に文を記述できます。ただし、リスト描画では一般的ではない方法です。たとえば、{(() => { const items = []; for (let i = 0; i < products.length; i += 1) { const product = products[i]; items.push(<li key={product.id}>{product.name}</li>); } return items; })()}というJSX式は、配列をその場で作成して返します。これは構文を示す例であり、実行済みのReact例ではありません。返す値はreturnと同じ行に書くか、括弧で囲んでください。returnの直後に改行すると、自動セミコロン挿入によってJavaScriptがundefinedを返すことがあります。通常のリストでは、.map()を使うか、コンポーネントのreturnより前にループを書くほうが読みやすく、保守もしやすくなります。