Рендер списков
Часто нам нужно отобразить несколько похожих компонентов из коллекции данных. Вы можете использовать методы массивов JavaScript для манипулирования массивом данных. На этой странице вы будете использовать filter()
и map()
с React для фильтрации и преобразования массива данных в массив компонентов.
You will learn
- Как рендерить компоненты из массива, используя
map()
- Как рендерить только определенные компоненты, используя
filter()
- Когда и почему использовать React ключи
Рендер данных из массивов
Предположим, у вас есть список контента.
<ul>
<li>Креола Кэтрин Джонсон (Creola Katherine Johnson): математик</li>
<li>Марио Молина (Mario José Molina-Pasquel Henríquez): химик</li>
<li>Мухаммад Абдус Салам (Mohammad Abdus Salam): физик</li>
<li>Перси Джулиан (Percy Lavon Julian): химик</li>
<li>Субраманьян Чандрасекар (Subrahmanyan Chandrasekhar): астрофизик</li>
</ul>
Единственная разница между этими элементами списка - их содержимое, их данные. При построении интерфейсов вам часто нужно показывать несколько экземпляров одного и того же компонента, используя различные данные: от списков комментариев до галерей профилей. В таких ситуациях вы можете хранить эти данные в объектах и массивах JavaScript и использовать методы, такие как map()
и filter()
, чтобы рендерить списки компонентов с данными из них.
Вот короткий пример того, как сгенерировать список элементов из массива:
- Переместите данные в массив:
const people = [
'Креола Кэтрин Джонсон (Creola Katherine Johnson): математик',
'Марио Молина (Mario José Molina-Pasquel Henríquez): химик',
'Мухаммад Абдус Салам (Mohammad Abdus Salam): физик',
'Перси Джулиан (Percy Lavon Julian): химик',
'Субраманьян Чандрасекар (Subrahmanyan Chandrasekhar): астрофизик'
];
- Преобразуйте элементы массива
people
в новый массив JSX-узлов,listItems
, используя методmap()
:
const listItems = people.map(person => <li>{person}</li>);
- Верните
listItems
из вашего компонента, обернув их в тег<ul>
:
return <ul>{listItems}</ul>;
Вот что должно получиться в итоге:
const people = [ 'Креола Кэтрин Джонсон (Creola Katherine Johnson): математик', 'Марио Молина (Mario José Molina-Pasquel Henríquez): химик', 'Мухаммад Абдус Салам (Mohammad Abdus Salam): физик', 'Перси Джулиан (Percy Lavon Julian): химик', 'Субраманьян Чандрасекар (Subrahmanyan Chandrasekhar): астрофизик' ]; export default function List() { const listItems = people.map(person => <li>{person}</li> ); return <ul>{listItems}</ul>; }
Обратите внимание, что в консоли песочницы отображается ошибка:
Вы узнаете о том, как исправить эту ошибку позже на этой странице. Прежде чем перейти к этому, давайте добавим некоторую структуру к данным.
Фильтрация массивов элементов
Структуру этих данных можно улучшить.
const people = [{
id: 0,
name: 'Креола Кэтрин Джонсон (Creola Katherine Johnson)',
profession: 'математик',
}, {
id: 1,
name: 'Марио Молина (Mario José Molina-Pasquel Henríquez)',
profession: 'химик',
}, {
id: 2,
name: 'Мухаммад Абдус Салам (Mohammad Abdus Salam)',
profession: 'физик',
}, {
name: 'Перси Джулиан (Percy Lavon Julian)',
profession: 'химик',
}, {
name: 'Субраманьян Чандрасекар (Subrahmanyan Chandrasekhar)',
profession: 'астрофизик',
}];
Допустим, вам нужен способ отображать только людей, чья профессия 'химик'
. Вы можете использовать метод JavaScript filter()
чтобы вернуть только этих людей. Этот метод принимает массив элементов, пропускает их через «тест» (функция, которая возвращает true
или false
) и возвращает новый массив только из тех элементов, которые прошли тест (вернули true
).
В нашем случае мы хотим отобразить только те элементы, где profession
является 'химик'
. «Тест» для этого выглядит так: (person) => person.profession === 'химик'
. Вот как собрать все воедино:
- Создайте новый массив только из людей с профессией
'chemist'
, вызвавfilter()
наpeople
для фильтрации поperson.profession === 'химик'
:
const chemists = people.filter(person =>
person.profession === 'химик'
);
- Теперь преобразуйте элементы массива
chemists
:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}.
Достижение: {person.accomplishment}
</p>
</li>
);
- Наконец, верните
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 === 'химик' ); const listItems = chemists.map(person => <li> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '}. Достижение: {person.accomplishment} </p> </li> ); return <ul>{listItems}</ul>; }
Сохранение порядка элементов списка с помощью key
Заметьте, что все песочницы выше показывают ошибку в консоли:
Чтобы решить эту ошибку необходимо присвоить каждому элементу массива ключ (key
) — строку или число, которое уникально отличает данный элемент среди других элементов этого массива:
<li key={person.id}>...</li>
Ключи позволяют React узнать к какому элементу массива соответствует каждый компонент, чтобы позже сопоставить их. Это важно, если элементы массива могут перемещаться (например, из-за сортировки), добавляться или удаляться. Хорошо выбранный ключ помогает React понять, какое именно изменение произошло, и правильно обновить DOM дерево.
Вместо генерации ключей на лету, вы должны включать их в свои данные:
export const people = [{ id: 0, // Используется в JSX в качестве ключа name: 'Креола Кэтрин Джонсон (Creola Katherine Johnson)', profession: 'математик', accomplishment: 'расчёты для космических полетов', imageId: 'MK3eW3A' }, { id: 1, // Используется в JSX в качестве ключа name: 'Марио Молина (Mario José Molina-Pasquel Henríquez)', profession: 'химик', accomplishment: 'обнаружение дыр в озоновом слое', imageId: 'mynHUSa' }, { id: 2, // Используется в JSX в качестве ключа name: 'Мухаммад Абдус Салам (Mohammad Abdus Salam)', profession: 'физик', accomplishment: 'открытие теории электромагнетизма', imageId: 'bE7W1ji' }, { id: 3, // Используется в JSX в качестве ключа name: 'Перси Джулиан (Percy Lavon Julian)', profession: 'химик', accomplishment: 'изобретение препаратов с кортизоном, стероидов и противозачаточных таблеток', imageId: 'IOjWm71' }, { id: 4, // Используется в JSX в качестве ключа name: 'Субраманьян Чандрасекар (Subrahmanyan Chandrasekhar)', profession: 'астрофизик', accomplishment: 'расчёт массы белого карлика', imageId: 'lrWQx8l' }];
Deep Dive
Как поступить, если каждый элемент должен отображать не один, а несколько 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>
, и т.д.
Откуда взять ключ
Разные источники данных предоставляют разные источники для ключей:
-
Данные из базы данных: Если ваши данные приходят с базы данных, то вы можете использовать ключи/ID с базы данных, которые по своей природе уникальны.
-
Локальные данные: Если ваши данные генерируются и хранятся локально (к примеру, заметки в приложении для ведений заметок), используйте инкрементный счетчик,
crypto.randomUUID()
или пакетuuid
при создании элементов.
Правила ключей
- Ключи должны быть уникальны среди своих соседних элементов. Однако, можно использовать одинаковые ключи для JSX-узлов в разных массивах.
- Ключи не должны меняться, так как это лишает их смысла! Не генерируйте их во время рендеринга.
Почему React нужны ключи?
Представьте что у файлов на вашем рабочем столе не было бы имен. Взамен, вы бы ссылались на них по их порядку — первый файл, второй файл, и т.д. Возможно к этому и можно привыкнуть, но когда вы удалите какой-либо файл, порядок изменится и все станет запутанным. Второй файл станет первым, третий файл станет вторым, и т. д.
Названия файлов в папке и JSX ключи в массиве имеют схожую цель. Они позволяют нам отличать элементы от их других элементов в массиве. А хорошо выбранный ключ предоставляет больше информации, чем позиция в массиве. Даже если позиция изменится из-за смены порядка, key
позволит React идентифицировать элемент на протяжении всего существования элемента.
Recap
На этой странице вы узнали:
- Как перенести данные из компонентов в структуры данных, такие как массивы и объекты.
- Как создавать коллекции схожих компонентов с помощью JavaScript
map()
. - Как создавать массивы отфильтрованных элементов с помощью JavaScript
filter()
. - Зачем и как присваивать ключ каждому компоненту в коллекции, чтобы React мог отслеживать изменения каждого из них.
Challenge 1 of 4: Разделение списка на два
Этот пример показывает список всех людей в people
.
Поменяйте код так, чтобы он показывал два списка один за другим: Химики и Все остальные. Как и раньше, вы можете определить, является ли человек химиком, проверив person.profession === 'химик'
.
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 + ' '} Достижение: {person.accomplishment} </p> </li> ); return ( <article> <h1>Ученые</h1> <ul>{listItems}</ul> </article> ); }