Рендеринг списків
Часто потрібно показати кілька подібних компонентів із колекції даних. Ви можете використовувати методи JavaScript для масивів, щоб маніпулювати масивом даних. На цій сторінці ви використовуватиме filter()
і map()
разом з React для фільтрування та перетворення вашого масиву даних у масив компонентів.
You will learn
- Як рендерити компоненти з масиву, використовуючи
map()
з JavaScript - Як рендерити лиш деякі компоненти, використовуючи
filter()
з JavaScript - Коли і навіщо використовувати ключі React
Рендеринг масивів даних
Припустимо, у вас є список певних даних.
<ul>
<li>Кетрін Джонсон (Creola Katherine Johnson): математик</li>
<li>Маріо Моліна (Mario José Molina-Pasquel Henríquez): хімік</li>
<li>Абдус Салам (Moшинкаmad 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): хімік',
'Абдус Салам (Moшинкаmad Abdus Salam): фізик',
'Персі Джуліан (Percy Lavon Julian): хімік',
'Субрахманьян Чандрасекар (Subrahmanyan Chandrasekhar): астрофізик'
];
- Перетворіть члени масиву
people
у новий масив JSX-вузлів —listItems
:
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): хімік', 'Абдус Салам (Moшинкаmad 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: 'Абдус Салам (Moшинкаmad Abdus Salam)',
profession: 'фізик',
}, {
id: 3,
name: 'Персі Джуліан (Percy Lavon Julian)',
profession: 'хімік',
}, {
id: 4,
name: 'Субрахманьян Чандрасекар (Subrahmanyan Chandrasekhar)',
profession: 'астрофізик',
}];
Скажімо, вам потрібен спосіб показати лише людей з професією 'хімік'
. Ви можете використовувати JavaScript-метод filter()
, щоб отримати лише цих людей. Цей метод бере масив елементів, піддає їх “тестуванню” (функція, що повертає true
або false
) і повертає новий масив тільки тих елементів, які пройшли перевірку (повернули true
).
Вам потрібні елементи, де професія
— 'хімік'
. Тест-функція для цього виглядає так: (person) => person.profession === 'хімік'
. Ось усе разом:
- Створіть новий масив лише людей-хіміків,
chemists
, викликавши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, якому елементу масиву відповідає кожен компонент, щоб він міг зіставити їх пізніше. Це важливо, якщо елементи масиву можуть змінювати позицію (наприклад, через сортування), вставлятися або видалятися. Добре підібраний key
допомагає React визначити, що саме сталося, і правильно оновити дерево DOM.
Замість того, щоб генерувати ключі “на льоту”, вам слід додати їх до своїх даних:
export const people = [{ id: 0, // Used in JSX as a key name: 'Кетрін Джонсон (Creola Katherine Johnson)', profession: 'математик', accomplishment: 'розрахунки для космічних польотів', imageId: 'MK3eW3A' }, { id: 1, // Used in JSX as a key name: 'Маріо Моліна (Mario José Molina-Pasquel Henríquez)', profession: 'хімік', accomplishment: 'відкриття озонової діри в Арктиці', imageId: 'mynHUSa' }, { id: 2, // Used in JSX as a key name: 'Абдус Салам (Moшинкаmad Abdus Salam)', profession: 'фізик', accomplishment: 'теорія електромагнетизму', imageId: 'bE7W1ji' }, { id: 3, // Used in JSX as a key name: 'Персі Джуліан (Percy Lavon Julian)', profession: 'хімік', accomplishment: 'новаторські кортизоновмісні препарати, стероїди та протизаплідні таблетки', imageId: 'IOjWm71' }, { id: 4, // Used in JSX as a key 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>
і так далі.
Де взяти key
Різні джерела даних надають різні джерела ключів:
- Дані з бази даних: Якщо ваші дані надходять із бази даних, ви можете використовувати ключі/ідентифікатори бази даних, які є унікальними за своєю природою.
- Локально створені дані: Якщо ваші дані генеруються та зберігаються локально (наприклад, нотатки в застосунку для створення нотаток), використовуйте лічильник з інкрементом під час створення елементів —
crypto.randomUUID()
або пакет на зразокuuid.
Правила для ключів
- Ключі мають бути унікальними для елементів одного рівня. Проте можна використовувати однакові ключі для вузлів JSX у різних масивах.
- Ключі не повинні змінюватися, бо інакше це не відповідає їхньому призначенню! Не створюйте їх під час рендерингу.
Навіщо React потрібні ключі?
Уявіть, що файли на вашому робочому столі не мають імен. Замість цього ви посилаєтеся на них по порядку — перший файл, другий файл тощо. Ви можете звикнути до цього, але як тільки ви видалите файл, все сплутається. Другий файл стане першим файлом, третій файл стане другим файлом і так далі.
Імена файлів у каталозі та JSX-ключі у масиві мають схожу мету. Вони дозволяють нам унікально ідентифікувати об’єкт на одному рівні. Добре підібраний ключ надає більше інформації, ніж позиція у масиві. Навіть якщо позиція змінюється через під час зміни порядку, key
дозволяє React ідентифікувати елемент протягом усього його життя.
Recap
На цій сторінці ви дізналися:
- Як перемістити дані з компонентів у структури даних як масиви та об’єкти.
- Як створити набори подібних компонентів за допомогою
map()
з JavaScript. - Як створити масиви відфільтрованих елементів за допомогою
filter()
з JavaScript. - Навіщо і як задати
key
для кожного компонента в колекції, щоб React міг відстежувати кожен із них, навіть якщо їхня позиція чи дані зміняться.
Challenge 1 of 4: Розбиття одного списку на два
Цей приклад показує список усіх людей.
Змініть його, щоб відобразити один за одним два окремі списки: Хіміки та Усі інші. Як і раніше, ви можете визначити, чи є особа хіміком, із порівняння 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> ); }