Рендер списков
Часто возникает необходимость отображения ряда схожих компонентов на основе набора данных. Для этого можно воспользоваться методами массивов JavaScript для манипулирования массивом данных. На этой странице вы будете использовать filter()
и map()
с React для фильтрации и преобразования массива данных в массив компонентов.
Вы узнаете
- Как рендерить компоненты из массива, используя
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: 'физик',
}, {
id: 3,
name: 'Перси Джулиан (Percy Lavon Julian)',
profession: 'химик',
}, {
id: 4,
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> ); }