Зарегистрируйтесь, чтобы продолжить обучение

Работа с коллекциями JS: React

В работе с коллекциями элементов в 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>
    ];
  }
}

Дополнительные материалы

  1. Фрагменты в документации React

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Верстка на HTML5 и CSS3, Программирование на JavaScript в браузере, разработка клиентских приложений используя React
10 месяцев
с нуля
Старт 26 декабря
профессия
Программирование на JavaScript в браузере и на сервере (Node.js), разработка бекендов на Fastify и фронтенда на React
16 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»