Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Children JS: React

UI-элементы имеют иерархическую структуру. Например, компонент card в Bootstrap:

<div class="card">
  <img class="card-img-top" src="..." alt="Card image cap">
  <div class="card-body">
    <h4 class="card-title">Card title</h4>
    <p class="card-text">
      Some quick example text to build on the card title and make up the bulk of the card's content.
    </p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>

Блок карточки может содержать внутри себя картинку и тело. Тело, в свою очередь, может состоять из заголовка и текста, а текст может быть чем угодно. То же самое применимо как к самым простым элементам самого HTML, например, тегу <div>, так и к остальным компонентам Bootstrap, таким как модальные окна и навигация.

HTML соответствует природе UI и естественным образом позволяет создавать композиции элементов за счёт вкладывания тегов друг в друга. Точно так же работает и JSX. Пока эта возможность использовалась в курсе только для встроенных компонентов. Теперь пора научиться повторять подобное поведение в самописных компонентах. В качестве примера взят компонент <Alert /> из Bootstrap.

See the Pen YxgWVm by Hexlet (@hexlet) on CodePen.

В примере выше обязательной частью является только основной div. Содержимое зависит от конкретной ситуации. Подставляется оно с помощью свойства children.

See the Pen js_react_children_alert_component by Hexlet (@hexlet) on CodePen.

Обратите внимание на то, что компонент стал использоваться как парный тег в JSX:

const vdom = (
  <Alert>
    <p>Whenever you need to, be sure to use margin utilities to keep things nice and tidy.</p>
  </Alert>
);

Все, что находится между открывающим и закрывающим тегом, попадает внутрь пропса children.

Но будьте бдительны: тип данных свойства children зависит от содержимого. В простейшем случае, когда тег используется как одиночный <div />, это свойство будет равно undefined.

Если этим содержимым является строка, то именно она окажется внутри children. Правда, после некоторой обработки. JSX удаляет пробельные символы с начала и конца строки, включая пустые строки. Следующие примеры будут отображены одинаково:

<div>Hello World</div>

<div>
  Hello World
</div>

<div>
  Hello
  World
</div>

<div>

  Hello World
</div>

Любой одиночный дочерний компонент также будет представлен сам собой в children. Во всех остальных случаях children будет содержать массив.

Если внимательно посмотреть на документацию React, то можно увидеть следующее определение children: "children are an opaque data structure" (свойство children – непрозрачная структура данных). Другими словами, нельзя однозначно полагаться на тип этого пропса, так как снаружи можно передать всё что угодно.

Подобное поведение может приводить к трудноотловимым ошибкам. Например, проверка this.props.children.length — это не всегда количество детей. Если children это одиночный элемент, например строка, то свойство length вернет длину этой строки.

class MyComponent extends React.Component {
  render() {
    const { children } = this.props
    return <p>Count: {children.length}</p>
  }
}

// <p>Count: 4</p>
<MyComponent>Text</MyComponent>

Именно поэтому, React предоставляет набор функций предназначенных для манипулирования пропсом children. Все они доступны в React.Children. Эти функции знают про особенности работы children, сами проверяют тип и делают нужные проверки в зависимости от типа данных.

React.Children.map()

See the Pen js_react_children_map by Hexlet (@hexlet) on CodePen.

Из урока про обработку коллекций вы можете вспомнить, что при работе со списком каждому элементу нужно установить проп key. Если этого не сделать в случае с React.Children.map, React не выбросит предупреждение. Так сделано намеренно, поскольку у потомков обычно нет уникальных идентификаторов.

React.Children.count()

Чтобы получить количество детей, нужно использовать метод React.Children.count(). В него передается children. Узлы, которые не являются DOM-элементами, будут проигнорированы:

class ChildrenCounter extends React.Component {
  render() {
    const { children } = this.props
    return <p>Count: {React.Children.count(children)}</p>
  }
}

// Count: 1
<ChildrenCounter>
  Second!
</ChildrenCounter>

// Count: 2
<ChildrenCounter>
  <p>First</p>
  <ChildComponent />
</ChildrenCounter>

// Count: 2
<ChildrenCounter>
  {() => <h1>First!</h1>} // будет пропущено, поскольку не является dom-элементом
  Second!
  <p>Third!</p>
</ChildrenCounter>

Кроме перечисленного выше, бывает необходимо обработать дочерние элементы перед выводом, изменив часть в пропсе. Конечно же, напрямую этого сделать нельзя, ведь пропсы неизменяемы. Такого поведения можно добиться, клонируя элементы функцией React.cloneElement().

Композиция компонентов

Иерархия компонентов во многом закладывает структуру приложения. То, как вы распределите приложение на компоненты определит кодовую базу вашего проекта.

Есть несколько советов как разбивать приложение на компоненты:

  • выделите части интерфейса в приложении, которые имеют осмысленный функционал. Например: навигационная панель, боковое меню, окно чата и т.д. Каждую такую часть приложения можно выделить в отдельный компонент. Такое разбиение интерфейса на составляющие части заложит основу компонентной базы
  • если какие-то элементы повторяются, то их тоже лучше выделить в отдельный компонент для переиспользования. Например это могут быть Button и InputField
  • рассматривайте компоненты как чистые функции. Пропсы, как и параметры в чистых функциях, не должны изменяться в компонентах, в React это важное правило

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

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

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

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

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка фронтенд-компонентов для веб-приложений
10 месяцев
с нуля
Старт 1 июня
профессия
от 10 080 ₽ в месяц
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
16 месяцев
с нуля
Старт 1 июня

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

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

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

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