Во-первых, давайте рассмотрим, как мы преобразуем списки в JavaScript.
Учитывая приведенный ниже код, мы используем функцию map(), чтобы принять массив чисел и удвоить их значения. Мы назначаем новому массиву, результат возвращенный из map() и выводим в консоль:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);
В консоли будет написано [2, 4, 6, 8, 10]
.
В React, преобразование массива в список элементов почти идентично.
Рендеринг нескольких компонентов
Вы можете построить коллекцию элементов и включить их в JSX используя фигурные скобки {}
.
Ниже, мы циклически проходим по массиву чисел с помощью функции Javascript map(). Мы возвращаем элемент <li> для каждого пункта. И, наконец, мы присваиваем результирующий массив элементов в listItems
:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
Мы включаем весь массив listItems
внутрь элемента <ul>
, и отрисовываем его в DOM:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
Этот код отобразит ненумерованный список чисел от 1 до 5.
Базовый компонент списка
Обычно мы будем отрисовывать список в компонент.
Мы можем реорганизовать предыдущий пример в компонент, который принимает массив numbers
и выводит маркированный список элементов.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
При выполнении этого кода, вам будет выдано предупреждение о том, что для элементов списка должен быть обеспечен ключ. «Ключ» представляет собой специальный строковый атрибут который необходимо включить при создании списка элементов. Почему это важно, мы обсудим в следующем разделе.
Зададим key
пунктам нашего списка внутри numbers.map()
.
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Ключи
Ключи помогают React определять какие из пунктов были изменены, добавлены или удалены. Ключи следует назначать элементам массива, чтобы они были проидентифицированы:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
Лучший способ: подобрать ключ в виде строки, которая однозначно идентифицирует элемент списка среди соседних элементов. Чаще всего, в качестве ключей вы будете использовать идентификаторы из ваших данных:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
Если у вас нет постоянных идентификаторов для отрисовки пунктов, в крайнем случае вы можете использовать их индекс:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
Мы не рекомендуем использовать индексы в качестве ключей, потому как элементы могут изменить порядок и все будет работать медленно. Если Вам интересно, можете прочитать подробные разъяснения о том, почему ключи необходимы.
Извлечение компонентов с ключами
Ключи имеют смысл только в контексте окружающего массива.
Например, если вы извлекаете компонент ListItem
, вы должны сохранить ключ на элементах <ListItem />
в массиве, а не на корень <li> элемента в ListItem
itself.
Пример: Некорректное использование ключа
function ListItem(props) {
const value = props.value;
return (
// Неправильно! Нет необходимости указывать ключ здесь:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Неправильно! Ключ должен быть указан здесь:
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
Пример: корректной использование ключа
function ListItem(props) {
// Правильно! Нет необходимости указывать здесь ключ:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// Правильно! Ключ указывается тут:
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
A good rule of thumb is that elements inside the map()
call need keys.
Ключи должны только быть уникальным среди соседних элементов
Ключи, используемые в массивах должны быть уникальными среди своих соседних элементов. Тем не менее, они не должны быть уникальными глобально. Мы можем использовать одни и те же ключи, когда создаем два разных массива:
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);
Ключи служат в качестве подсказки для React, но они не передаются к компонентам. Если вам нужно такое же значение в компоненте, можно передать его в явном виде в качестве свойства с другим именем:
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
С помощью приведенного выше примера, компонент может читать сообщение props.id
, но не props.key
.
Размещение map() в JSX
В приведенных выше примерах мы объявили отдельную переменную listItems
и включила её в JSX:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
JSX позволяет внедрять любые выражения в фигурные скобки, чтобы мы могли встраивать результат map():
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
Иногда это приводит к чистоте кода, но этим стилем также можно злоупотреблять. Как и в JavaScript, это должно быть Вашим решением, стоит ли извлекая переменную. Имейте в виду, что если содержимое map()
имеет большую вложенность, это может быть моментом чтобы извлечь компонент.