リストのレンダリング

多くの場合、データのコレクションから複数の類似したコンポーネントを表示したいと思うでしょう。JavaScript の配列メソッドを使用して、データの配列を操作できます。このページでは、React で filter()map() を使用して、データの配列をフィルタリングおよび変換し、コンポーネントの配列に変換する方法を学びます。JavaScript 配列メソッド

学習内容

  • JavaScript の map() を使用して配列からコンポーネントをレンダリングする方法
  • JavaScript の filter() を使用して特定のコンポーネントのみをレンダリングする方法
  • React キーを使用するタイミングと理由

配列からのデータのレンダリング

コンテンツのリストがあるとします。

<ul>
<li>Creola Katherine Johnson: mathematician</li>
<li>Mario José Molina-Pasquel Henríquez: chemist</li>
<li>Mohammad Abdus Salam: physicist</li>
<li>Percy Lavon Julian: chemist</li>
<li>Subrahmanyan Chandrasekhar: astrophysicist</li>
</ul>

これらのリスト項目の違いは、コンテンツ、つまりデータだけです。インターフェースを構築する際には、コメントのリストからプロフィール画像のギャラリーまで、異なるデータを使用して同じコンポーネントの複数のインスタンスを表示する必要があることがよくあります。このような状況では、そのデータを JavaScript のオブジェクトと配列に格納し、map()filter() などのメソッドを使用して、それらからコンポーネントのリストをレンダリングできます。

配列から項目のリストを生成する簡単な例を以下に示します。

  1. 移動 データを配列に移動します
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
  1. マップ people メンバーを JSX ノードの新しい配列 listItems にマップします
const listItems = people.map(person => <li>{person}</li>);
  1. 戻り値 コンポーネントから listItems<ul> で囲んで返します
return <ul>{listItems}</ul>;

結果はこちらです

const people = [
  'Creola Katherine Johnson: mathematician',
  'Mario José Molina-Pasquel Henríquez: chemist',
  'Mohammad Abdus Salam: physicist',
  'Percy Lavon Julian: chemist',
  'Subrahmanyan Chandrasekhar: astrophysicist'
];

export default function List() {
  const listItems = people.map(person =>
    <li>{person}</li>
  );
  return <ul>{listItems}</ul>;
}

上記のサンドボックスにコンソールエラーが表示されていることに注意してください

コンソール
警告: リスト内の各子要素には一意の "key" prop が必要です。

このエラーの修正方法については、このページの後半で説明します。その前に、データに構造を追加しましょう。

項目の配列のフィルタリング

このデータはさらに構造化できます。

const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];

職業が 'chemist' の人のみを表示する方法が必要だとします。JavaScript の filter() メソッドを使用して、それらの人だけを返すことができます。このメソッドは、項目の配列を受け取り、「テスト」(true または false を返す関数)に通し、テストに合格した(true を返した)項目のみの新しい配列を返します。

profession'chemist' である項目のみが必要な場合、「テスト」関数は (person) => person.profession === 'chemist' のようになります。以下にまとめ方を示します。

  1. 作成 peoplefilter() を呼び出して、person.profession === 'chemist' でフィルタリングすることにより、「化学者」のみの新しい配列 chemists を作成します
const chemists = people.filter(person =>
person.profession === 'chemist'
);
  1. 次に、マップ chemists をマップします
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
  1. 最後に、コンポーネントから listItems返します
return <ul>{listItems}</ul>;
import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const chemists = people.filter(person =>
    person.profession === 'chemist'
  );
  const listItems = chemists.map(person =>
    <li>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

落とし穴

アロー関数は => の直後の式を暗黙的に返すため、return 文は必要ありませんでした

const listItems = chemists.map(person =>
<li>...</li> // Implicit return!
);

ただし、`=>` の後に `{` 中括弧が続く場合は、`return` を明示的に記述する必要があります!

const listItems = chemists.map(person => { // Curly brace
return <li>...</li>;
});

`=> {` を含むアロー関数は、「ブロック本体」を持つと言われています。複数行のコードを記述できますが、`return` 文を自分で記述する必要があります。 これを忘れると、何も返されません!

`key` を使用してリスト項目の順序を保持する

上記のすべてのサンドボックスは、コンソールにエラーを表示していることに注意してください。

コンソール
警告: リスト内の各子要素には一意の "key" prop が必要です。

各配列項目に `key` を付ける必要があります。これは、その配列内の他の項目の中で一意に識別する文字列または数値です。

<li key={person.id}>...</li>

注記

`map()` 呼び出しの直内にある JSX 要素には、常にキーが必要です!

キーは、各コンポーネントがどの配列項目に対応するかを React に伝え、後でそれらを一致させることができます。これは、配列項目が移動(例:ソートによる)、挿入、または削除される可能性がある場合に重要になります。適切に選択された `key` は、React が何が起こったかを正確に推測し、DOM ツリーを正しく更新するのに役立ちます。

キーをその場で生成するのではなく、データに含める必要があります。

export const people = [{
  id: 0, // Used in JSX as a key
  name: 'Creola Katherine Johnson',
  profession: 'mathematician',
  accomplishment: 'spaceflight calculations',
  imageId: 'MK3eW3A'
}, {
  id: 1, // Used in JSX as a key
  name: 'Mario José Molina-Pasquel Henríquez',
  profession: 'chemist',
  accomplishment: 'discovery of Arctic ozone hole',
  imageId: 'mynHUSa'
}, {
  id: 2, // Used in JSX as a key
  name: 'Mohammad Abdus Salam',
  profession: 'physicist',
  accomplishment: 'electromagnetism theory',
  imageId: 'bE7W1ji'
}, {
  id: 3, // Used in JSX as a key
  name: 'Percy Lavon Julian',
  profession: 'chemist',
  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
  imageId: 'IOjWm71'
}, {
  id: 4, // Used in JSX as a key
  name: 'Subrahmanyan Chandrasekhar',
  profession: 'astrophysicist',
  accomplishment: 'white dwarf star mass calculations',
  imageId: 'lrWQx8l'
}];

詳細

リスト項目ごとに複数の DOM ノードを表示する...

各項目が 1 つではなく、複数の DOM ノードをレンダリングする必要がある場合はどうすればよいでしょうか?

短い <>...</> フラグメント 構文ではキーを渡すことができないため、単一の `<div>` にグループ化するか、少し長く、より明示的な `<Fragment>` 構文を使用する必要があります。

import { Fragment } from 'react';

// ...

const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);

フラグメントは DOM から消えるため、これは `<h1>`、`<p>`、`<h1>`、`<p>` などのフラットなリストを生成します.

`key` を取得する場所...

データソースが異なれば、キーのソースも異なります。

  • データベースからのデータ: データがデータベースからのものである場合は、本質的に一意であるデータベースキー/ ID を使用できます。
  • ローカルで生成されたデータ: データがローカルで生成および永続化される場合(例:メモ帳アプリのメモ)、インクリメントカウンター、`crypto.randomUUID()`、または `uuid` のようなパッケージを項目の作成時に使用します。

キーのルール...

  • キーは兄弟間で一意である必要があります。 ただし、異なる配列の JSX ノードに同じキーを使用しても問題ありません。
  • キーは変更しないでください。変更すると、目的が損なわれます! レンダリング中に生成しないでください。

React にキーが必要な理由...

デスクトップ上のファイルに名前がないと想像してみてください。代わりに、順序(最初のファイル、2 番目のファイルなど)で参照します。慣れることはできますが、ファイルを削除すると混乱が生じます。2 番目のファイルが最初のファイルになり、3 番目のファイルが 2 番目のファイルになり、というようになります。

フォルダ内のファイル名と配列内の JSX キーは、同様の役割を果たします。これらにより、兄弟要素の中で項目を一意に識別できます。適切に選択されたキーは、配列内の位置よりも多くの情報を提供します。並べ替えによって *位置* が変更された場合でも、key を使用すると、React はそのライフタイムを通じて項目を識別できます。

落とし穴

項目の配列内でのインデックスをキーとして使用したくなるかもしれません。実際、key をまったく指定しない場合、React はそれを使用します。ただし、項目が挿入、削除、または配列が並べ替えられた場合、項目をレンダリングする順序は時間の経過とともに変化します。インデックスをキーとして使用すると、微妙で分かりにくいバグが発生することがよくあります。

同様に、key={Math.random()} などで、その場でキーを生成しないでください。これにより、レンダリング間でキーが一致しなくなり、すべてのコンポーネントと DOM が毎回再作成されることになります。これは遅いだけでなく、リスト項目内のユーザー入力が失われます。代わりに、データに基づいた安定した ID を使用してください。

コンポーネントは、key を props として受け取らないことに注意してください。これは React 自体によってヒントとしてのみ使用されます。コンポーネントに ID が必要な場合は、それを別の props として渡す必要があります:<Profile key={id} userId={id} />

まとめ...

このページでは、以下のことを学びました

  • コンポーネントから配列やオブジェクトなどのデータ構造にデータを移動する方法。
  • JavaScript の map() を使用して、類似したコンポーネントのセットを生成する方法。
  • JavaScript の filter() を使用して、フィルタリングされた項目の配列を作成する方法。
  • コレクション内の各コンポーネントに key を設定する理由と方法。これにより、React は位置やデータが変更された場合でも、各コンポーネントを追跡できます。

チャレンジ 1/ 4:
リストを2つに分割する...

この例は、すべての人物のリストを示しています。

これを変更して、**化学者**と**その他全員**の2つの別々のリストを連続して表示します。 前と同様に、person.profession === 'chemist' をチェックすることで、人物が化学者かどうかを判断できます。

import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}