Когда я впервые начал разработку с React, у меня было поверхностное понимание композиции и мощи модели компонентов React, но я часто сталкивался с одной и той же проблемой снова и снова - боль от необходимости передавать состояние вниз через каждую ветвь моей модели. дерево компонентов в попытке реализовать какой-то конкретный шаблон пользовательского интерфейса. Бьюсь об заклад, вы испытали то же самое. Чтобы проиллюстрировать проблему, рассмотрим, как я передаю опору friends через дерево компонентов в приведенном ниже коде.

const UserProfile = ({ friends, displayName }) => (
  <div>
    <h1>{displayName}</h1>
    <UserFriends friends={friends} />
  </div>
)
const UserFriends = ({ friends }) => (
  <div>
    <h2>Friends</h2>
    {friends.map(friend => (
      <Friend friend={friend} key={friend.id} />
    ))}
  </div>
)
const Friend = ({ friend }) => (
  <div>
    <img src={profile.image} />
    <div>{friend.name}</div>
  </div>
)

Это небольшой пример, демонстрирующий этот конкретный шаблон компонента, но вы можете представить, что дерево компонентов имеет еще больше ветвей с состоянием, которое необходимо передать полностью вниз. Шаблон работает, но вы в конечном итоге расстраиваетесь из-за всего ненужного шаблона. Вы можете даже перепутать названия реквизита. И поэтому вы неизбежно стремитесь использовать что-то вроде React Context или, что более вероятно, инструмент вроде Redux для обработки универсальных подписок. Этим инструментам, безусловно, есть место, но мы склонны обращаться к ним слишком рано и часто.

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

const UserProfile = ({ friends, displayName }) => (
  <div>
    <h1>{displayName}</h1>
    <ProfileSection label='Friends'>
      {friends.map(friend => (
        <Friend friend={friend} key={friend.id} />
      ))}
    </ProfileSection>
  </div>
)
const ProfileSection = ({ label, children }) => (
  <div>
    <h2>{label}</h2>
    {children}
  </div>
)
const Friend = ({ friend }) => (
  <div>
    <img src={profile.image} />
    <div>{friend.name}</div>
  </div>
)

Это относительно простое изменение, но оно дает понять. Мы используем детей для обобщения нашего ProfileSection компонента и избегаем необходимости передавать массив друзей через несколько компонентов. В этом случае преимущество может показаться ограниченным, но основное преимущество состоит в том, что мы начинаем создавать более гибкие, повторно используемые компоненты. Давайте продолжим этот пример и предположим, что мы хотим позволить пользователю удалить существующую дружбу из своего списка.

const Friend = ({ friend, children }) => (
  <div>
    <img src={profile.image} />
    <div>{friend.name}</div>
    {children}
  </div>
)
const UserProfile = ({ friends, displayName, isUser }) => (
  <div>
    <h1>{displayName}</h1>
    <ProfileSection label='Friends'>
      {friends.map(friend => (
        <Friend friend={friend} key={friend.id}>
          {isUser && (
            <Button onClick={editFriendship}>
              Unfriend {friend.name}
            </Button>
          )}
        </Friend>
      ))}
    </ProfileSection>
  </div>
)

В этом контексте начинает проявляться реальная сила этого паттерна. Мы не только избегаем головной боли, связанной с передачей еще большего количества свойств вниз по дереву компонентов (в частности, isUser prop), но, что более важно, мы создаем гибкие, повторно используемые компоненты. Если вы обнаружите, что ваш UserProfile компонент становится слишком сложным, вы все равно можете инкапсулировать этот конкретный шаблон пользовательского интерфейса в его собственный компонент, но вы по-прежнему сохраняете преимущество возможности собирать другие компоненты из его дочерних компонентов.

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

Следуйте за мной в Twitter, чтобы получить больше советов по React!

Пожалуйста, нажмите кнопку 👏 ниже несколько раз, чтобы выразить свою поддержку! ⬇⬇