[Перевод] Пишем API для React компонентов, часть 5: просто используйте композицию

Пишем API для React компонентов, часть 1: не создавайте конфликтующие пропсы

Пишем API для React компонентов, часть 2: давайте названия поведению, а не способам взаимодействия

Пишем API для React компонентов, часть 3: порядок пропсов важен

Пишем API для React компонентов, часть 4: опасайтесь Апропакалипсиса!

Пишем API для React компонентов, часть 5: просто используйте композицию

У нас есть компонент значка:

badge-1

Вы видели их в различных приложениях, они показывают количество объектов в виде числа.

github-1

В cosmos Badge (значок) имеет несколько цветов для каждого конкретного контекста (информация, опасность и т.д.)

badge-2





У этого пользовательского интерфейса есть еще один похожий компонент — Label.

github-2

У него то же есть несколько цветов для каждого контекста:

label-2

Посмотрите на эти два компонента и скажите одну хорошую и одну плохую вещь об их API (об их пропсах)

together 2



Что хорошо

У обоих компонентов есть одинаковый проп для внешнего вида: appearance, это здорово. Мало того, у них одинаковые варианты для этого пропа! Если вы знаете как использовать appearance в Badge, то вы уже знаете как использовать appearance в Label


Стремитесь к последовательным пропсам между компонентами

Совет № 2 из Пишем API для React компонентов, часть 2: давайте названия поведению, а не способам взаимодействия


Что плохо

То, как они принимают свои значения, отличается. У них обоих свой вариант.

Подсчет — count, имеет смысл в рамках компонента Badge, но с учетом всех остальных ваших компонентов это дополнительный API о котором придется помнить вашей команде и пользователям (разработчикам).


Давайте улучшим этот API

Чтобы бы быть последовательным, я назову этот проп content, это наиболее общее название которое я смог придумать, — более общее чем просто label, text или value.

together 2


Мы потеряли некоторую детализацию, но получили большую последовательность. Мы все еще можем установить тип значения с помощью prop-types, так что думаю, что это хороший компромисс.

Но подождите, в React-е уже есть многоцелевой content проп, он называется children — дочерний.


Не переизобретайте props.children.

Если вы определили пропсы, которые принимают произвольные данные, не основанные на структуре данных, вероятно, лучше использовать composition (композицию) — Brent Jackson

Вот совет этой статьи — При выборе между композицией и пропсами, выбирайте композицию.

Давайте проведем рефакторинг этого API при помощи children — дочерних элементов:

together 2

12      

Выглядит отлично.

Бонус: когда вы вместо пропа text используете children, разработчик использующий этот компонент получает больше гибкости без необходимости изменять компонент.

К примеру, вот сообщение предупреждение, в нем, я хочу добавить иконку перед текстом.

alert

Используя children я могу добавить иконку в это сообщение предупреждение, не возвращаясь к этому компоненту и не меняя его.

// Плохо - приходиться добавлять поддержку иконок


// Хорошо

   This is an important message!

По совпадению, когда я писал этот текст, я увидел твит Брэда Фроста:


Эй, React друзья, нужна небольшая помощь. Я продолжаю сталкиваться с этим шаблоном, где определенные компоненты (особенно списки) могут быть разделены на более мелкие компоненты или управляться путем передачи объекта. Какой из вариантов лучше?

code

Выглядит знакомо?

Прежде всего, давайте не будем использовать проп text и вместо этого будем использовать children.

// вместо этого:


// напишем это:
Home

Теперь, когда мы разобрались с этим, давайте поговорим об этих двух вариантах API.

Как не сложно догадаться, мне нравится первый.


  1. Вам не нужно думать о том как называется проп — text? label? Это просто children.
  2. Вы можете добавить свое className или target к нему, если нужно. Для второго варианта вам нужно убедиться, что он поддерживает эти свойства или просто передает их базовому элементу.
  3. Это позволяет обернуть дочерний элемент в контекст или в компонент более высокого уровня.


Исключение из правила:

Что, если Брэд хочет запретить разработчику выполнять какие-либо настройки, о которых я упоминал выше? Тогда давать разработчику больше гибкости, в его случае, будет ошибкой!

Вот мой ответ Брэду.


Больше примеров

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

Формы — отличный пример использования, мы хотим управлять макетом формы, отображать ошибки и т.д. Но в то же время мы не хотим лишаться возможностей для расширения.

// #1 Плохо

// к чему относится этот id,
// к label или к input?

// #2 Хорошо

  
  


// #3 то же хорошо

  

Последний пример особенно интересный.

Иногда вам нужен компонент который будет использоваться в очень разных ситуациях. Не просто сделать компонент который будет гибок, и при этом все еще иметь простой API.

Вот где на помощь приходит инверсия управления — пусть пользователь компонента сам решает что рендерить. В мире React-а этот паттерн называется render prop pattern (паттерн рендер-пропсов).


Компонент с рендер-пропом берёт функцию, которая возвращает React-элемент, и вызывает её вместо реализации собственного рендера.

из документации React Рендер-пропсы

Одним из наиболее популярных примеров рендер-пропсов является официальный Context API.

В следующем примере компонент App контролирует данные, но не контролирует их рендеринг, он передает этот контроль компоненту Counter (счетчик).

// создаем новый контекст
const MyContext = React.createContext()

// значение передается вниз
// через контекст провайдер
function App() {
  return (
    
      
    
  )
}

// и потребляется через
// потребителя контекста
function Counter() {
  return (
    
      {value => (
        
the count is: {value}
)}
) }

Заметили что-нибудь интересное в этом Consumer API?

Вместо того чтобы создавать новый API, он использует children, чтобы принять функцию которая скажет ему как рендерить!

// Плохо
 (
  
the count is: {value}
)} /> // Хорошо {value => (
the count is: {value}
)}

Вернитесь к своему коду и найдите компонент который принимает какие либо пропсы, когда он, при этом, может легко использовать children.

© Habrahabr.ru