В работе с коллекциями элементов в JSX по большей части нет ничего особенного. С другой стороны, задача обработки списков элементов настолько частая, что будет не лишним её обсудить отдельно.
See the Pen js_react_jsx_collections by Hexlet (@hexlet) on CodePen.
Выше приведён типичный код, в котором коллекция генерируется прямо в том месте, куда и подставляется. Здесь можно снова увидеть, что внутрь JSX через {}
вложено выражение, внутри которого опять появляется JSX-код. Как правило, рекурсия на этом заканчивается. Если нужна более сложная обработка, то имеет смысл вынести генерацию коллекции в метод компонента и вызывать его внутри render
. Например, ниже выделена функция renderList()
, которая формирует список для отрисовки:
class List extends React.Component {
renderList = (data) => {
return data.map(item => <li>{item.name}</li>);
}
render() {
const { data } = this.props;
return (
<ul>
{this.renderList(data)}
</ul>
);
}
}
Проп key
Для повышения эффективности, React настоятельно рекомендует идентифицировать каждую генерируемую строку коллекции. Связано это с механизмом, который производит изменения в DOM. Подробнее об этом будет рассказано позже, а сейчас нужно просто запомнить, что, генерируя коллекцию элементов в JSX, нужно обязательно проставлять уникальный проп key
, который не меняется при повторной генерации коллекции. key
не обязан быть уникальным в глобальном контексте, достаточно уникальности среди соседних элементов.
Чаще всего с этой задачей не возникает проблем, так как у любой сущности, с которой мы работаем, есть свой идентификатор (например, primary key из базы данных).
class List extends React.Component {
render() {
const { data } = this.props;
return (
<ul>
{data.map((item) => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
}
const items = [
{ name: 'first', id: 1 },
{ name: 'second', id: 2 }
];
const mountNode = document.getElementById('react-root');
const root = ReactDOM.createRoot(mountNode);
root.render(<List data={items} />);
Как видите, ничего сложного в этом нет. Более того, если по какой-то причине вы забудете указать key
в коллекции, то React
начнёт выбрасывать предупреждения об этом прямо в консоли браузера. Поэтому пытаться запомнить все ситуации, в которых их надо ставить, не обязательно. В процессе работы вы и так об этом узнаете и сможете легко поправить.
Кстати, key
не обрабатывается как обычный проп и его нельзя получить внутри компонента как this.props.key
. Если вам нужны данные, которые были переданы в key
внутри компонента, то просто передайте их отдельным пропом (например, id
):
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title}
/>
);
// пример использования переменной content
class PostList extends React.Component {
render() {
return (
<div>
<h1>Список постов</h1>
{content}
</div>
);
}
Проп key
ставится только на генерируемые элементы коллекции. На элементы, которые сразу добавлены в шаблон, key
указывать не нужно:
class List extends React.Component {
render() {
const { data } = this.props;
return (
<ul>
{data.map((item) => <li key={item.id}>{item.name}</li>)}
<li>Элемент без key</li>
<li>Ещё один элемент без key</li>
</ul>
);
}
}
Корневой элемент компонента
Распространённой задачей является возврат нескольких элементов без общего родителя из одного компонента в другой. Допустим, одна статья содержит несколько подзаголовков, тогда её код будет выглядеть примерно так:
class Article extends React.Component {
render() {
return (
<article>
<h1>Заголовок статьи</h1>
<Section header={'Подзаголовок'} body={'Контент'} />
</article>
);
}
}
<Section />
должен объединить и вернуть несколько элементов. Если использовать для этих целей div
, как родительский элемент, то он попадёт в итоговый HTML:
class Section extends React.Component {
render() {
const { header, body } = this.props;
return (
<div>
<h2>{header}</h2>
<div>{body}</div>
</div>
);
}
}
Итоговый HTML из компонента <Article />
:
<article>
<h1>Заголовок статьи</h1>
<div>
<h2>Подзаголовок</h2>
<div>Контент</div>
</div>
</article>
Для решения этой задачи в React ввели специальный компонент <React.Fragment>
, которым можно оборачивать любую коллекцию элементов. Его особенность в том, что этот элемент никак не отражается в реальном DOM, а существует только на уровне JSX и может принимать единственный атрибут key
.
class Section extends React.Component {
render() {
const { header, body } = this.props;
return (
<React.Fragment>
<h2>{header}</h2>
<div>{body}</div>
</React.Fragment>
);
}
}
Тогда результатом вывода <Article />
будет:
<article>
<h1>Заголовок статьи</h1>
<h2>Подзаголовок</h2>
<div>Контент</div>
</article>
У этого элемента есть короткая версия <>
записи, но она не поддерживает ключи или атрибуты:
class Section extends React.Component {
render() {
const { header, body } = this.props;
return (
<>
<h2>{header}</h2>
<div>{body}</div>
</>
);
}
}
Выглядит непривычно, но работает отлично! :D
В случае когда элементы не нужно генерировать, можно вообще обойтись без React.Fragment, обернув все элементы в массив:
class Section extends React.Component {
render() {
return [
<h2 key='header'>Header</h2>,
<div key='body'>Body</div>
];
}
}
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.