В реальных приложениях на Реакте компонентов значительно больше. Часть из них — самостоятельные, часть используется только в составе других.

Один из способов компоновки компонентов мы уже знаем - children. Причем, нет никакой разницы, являются ли потомки встроенными в Реакт компонентами, или это написанные отдельно компоненты.

class Alert extends React.Component {
  render() {
    return (<div className="alert alert-primary">
      {this.props.children}
    </div>);
  }
}

const vdom = <Alert>
  <p>Paragraph 1</p>
  <hr />
  <p class="mb-0">Paragraph 2</p>
</Alert>;

ReactDOM.render(
  vdom,
  document.getElementById('react-root'),
);

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

<Card>
  <CardImgTop src="path/to/image" />
  <CardBody>
    <CardTitle>Body</CardTitle>
  </CardBody>
</Card>

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

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

Вкладывать можно сколько угодно раз и какие угодно компоненты. Но здесь кроется одна опасность. Желание построить "идеальную архитектуру" толкает разработчиков заранее планировать то, как разбить приложение на компоненты и сразу их реализовать. Важно понимать, что вложенность сама по себе — это усложнение понимания, так как придется постоянно прыгать туда сюда. Кроме того, жесткая структура свяжет вас по рукам и ногам, рефакторинг просто так не сделаешь, и желание его делать сильно поубавится из-за любви к своему решению. Будьте прагматичны. Оптимальный путь добавлять новые компоненты — это следить за моментом, когда вам становится сложно в текущем компоненте из-за объемов и количества переменных, с которыми приходится иметь дело одномоментно. И даже в этом случае часто достаточно выделить дополнительные функции рендеринга внутри самого компонента, например так: renderItem.

Состояние

Один из самых частых вопросов у тех, кто только начинает знакомиться с Реактом, связан с тем, как распределять состояние по компонентам. Короткий ответ: никак. Почти во всех ситуациях разделение состояния усложнит код и работу с ним. Правильный подход — создать корневой компонент, который содержит все состояние внутри себя, а все нижележащие компоненты получают свои данные как свойства. Само состояние должно быть максимально плоским, как реляционная база данных. Тогда можно спокойно применять нормализацию и безболезненно выполнять обновления.

Иногда могут возникать ситуации, что необходимые в глубине свойства приходится протаскивать сквозь множество промежуточных компонентов, которые сами эти свойства не используют. Это еще одна причина стараться не увлекаться глубокой вложенностью. С другой стороны, в следующем курсе мы познакомимся с Redux, который во многом решает эту проблему (и много других).

Колбеки

Из сказанного выше возникает еще одна сложность: что, если событие возникает в глубинном компоненте, у которого нет своего состояния? Без использования Redux выход, по сути, только один. Корневой компонент должен пробрасывать колбеки во внутренние компоненты, а те, в свою очередь, пробрасывают их дальше по необходимости.

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