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

 

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

Автор: admin от 12-07-2019, 16:35, посмотрело: 18

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



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



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



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



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

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



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



<Badge count={12} />


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



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



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

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

Что плохо



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



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



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



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



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



<Badge content="12" appearance="information" />
<Label content="private" appearance="information" />


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



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



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



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

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



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



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



<Badge appearance="information">12      </Badge>
<Label appearance="information">Private </Label>


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



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



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



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



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



// Плохо - приходиться добавлять поддержку иконок
[leech=https://twitter.com/brad_frost/status/1090733766950223878]твит Брэда Фроста[/leech]:</p>

[quote]Эй, React друзья, нужна небольшая помощь. Я продолжаю сталкиваться с этим шаблоном, где определенные компоненты (особенно списки) могут быть разделены на более мелкие компоненты или управляться путем передачи объекта. Какой из вариантов лучше?[/quote]<p>[img]https://habrastorage.org/webt/rb/rm/6k/rbrm6kcobqnadsodp6tck7rjpku.jpeg[/img]</p>

<p>Выглядит знакомо?</p>

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

[code]// вместо этого:
<Breadcrumb text="Home" href="/child" />

// напишем это:
<Breadcrumb href="/child">Home</Breadcrumb>


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



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




  1. Вам не нужно думать о том как называется проп — text? label? Это просто children.

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

  3. Это позволяет обернуть дочерний элемент в контекст или в компонент более высокого уровня.



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



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



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



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



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



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



// #1 Плохо
<FormTextInput
  type="text"
  label="Name"
  id="name-input"
/>
// к чему относится этот id,
// к label или к input?

// #2 Хорошо
<FormField>
  <Label>Field label</Label>
  <TextInput id="name-input" type="text" placeholder="What's your name?" />
</FormField>

// #3 то же хорошо
<FormField label="Field label">
  <TextInput id="name-input" type="text" placeholder="What's your name?" />
</FormField>


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



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



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



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



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

[/quote]

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



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



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

// значение передается вниз
// через контекст провайдер
function App() {
  return (
    <MyContext.Provider value="5">
      <Counter />
    </MyContext.Provider>
  )
}

// и потребляется через
// потребителя контекста
function Counter() {
  return (
    <MyContext.Consumer>
      {value => (
        <div className="counter">the count is: {value}</div>
      )}
    </MyContext.Consumer>
  )
}


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



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



// Плохо
<Consumer render={value => (
  <div className="counter">the count is: {value}</div>
)} />

// Хорошо
<Consumer>
  {value => (
    <div className="counter">the count is: {value}</div>
  )}
</Consumer>


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



Источник: Хабр / Интересные публикации

Категория: Веб-разработка

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

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *