Презентационный компонент и контейнер в React


Перевод "Presentational and Container Components" от Dan Abramov


 

Презентационный компонент и контейнер в React

Есть простой шаблон, который я нахожу очень полезным при написании приложений на React. Если вы какое-то время работаете с React, то, вероятно, вы о нем уже знаете. Данная статья хорошо объясняет это, но я бы хотел кое-что добавить.

Вам будете гораздо проще повторно использовать компоненты, если вы разделите их на две категории. Я называю их Контейнер и Презентационный компоненты*. Но также слышал Толстый и Худой, Умный и Глупый, Имеющий состояние и Чистый, соответсвенно. Они не совсем идентичны, но в основе лежит одна идея.

Мои презентационныe компоненты:

  • Интересует как части выглядят.
  • Могут содержать как презентационный, так и контейнеры-компоненты внутри, и обычно содержат некоторую собственную разметку DOM и стили.
  • Часто дает возможность обратиться через this.props.children.
  • Не зависит от остальной части приложения, таких как действия Flux или сторов.
  • Не используется для загрузки или изменения данных.
  • Получает данные и функции обратного вызова только через props.
  • Редко хранит свое состояние (обычно они относятся к интерфейсу, чем к другим данным).
  • Написаны как функциональные компоненты до тех пор пока не нуждаются в состоянии, использовании в жизненном цикле или оптимизации производительности.
  • Например, Page, Sidebar, Story, UserInfo, List.

Мой компоненты-контейнеры:

  • Интересует как части работают.
  • Могут содержать как презентационный, так и контейнеры-компоненты** внутри, но обычно не имеют разметки DOM, за исключение некоторых блоков div в качестве обертки, и не имеют никаких стилей.
  • Содержат данные или поведение презентационных или других компонентов-контейнеров.
  • Вызывают действия Flux и передают их в качестве функций обратного вызова в презентационные компоненты.
  • Часто имеют состояние и служат источником данных.
  • Чаще создаются с использованием компонентов высокого порядка, таких как connect() в React Redux, createContainer() в Relay или Container.create() в Flux Utils, чем пишутся вручную.
  • Например, UserPage, FollowersSidebar, StoryContainer, FollowedUserList.

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

Преимущества такого подхода

  • Лучшее разделение в решении проблем. Вы понимаете ваше приложение лучше при написании компонентов следуя подходу.
  • Лучше повторное использование. Вы можете использовать один и тот же компонент с совершенно разными источниками данных и превратить их в отдельный компонент-контейнер, который можно использовать повторно.
  • Презентационный компоненты являются “палитрой” вашего приложения. Вы можете вставить их на отдельную страницу и дать дизайнеру возможность настроить все их вариации минуя логику.
  • Это вынуждает извлечь “компоненты макета” как Sidebar, Page, ContextMenu и использовать this.props.children вместо дублирования одинаковой разметки и слоев в нескольких компонентах-контейнерах.

Помните, компоненты не должны извергать DOM. Они необходимы только для обеспечения структуры границ в проблеме интерфейса.

Воспользуемся этим.

Когда вводить контейнеры?

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

Рефакторинг осуществляется постоянно, поэтому не старайтесь написать все отлично с первого раза. Экспериментируя с этим паттерном, вы сможете развить интуитивное понимание, когда необходимо ввести контейнер, также , как вы знаете когда нужно извлечь функцию. Мой курс на Redux Egghead может помочь вам в этом!

Другое разделение

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

И наоборот, есть несколько связанных (но разных!) технических различий:

  • С состоянием и Без. Некоторые компоненты используют в React метод setState(), а некоторые нет. Если контейнеры склонны к наличию состояния, а презентационные компоненты нет, то это не является жестким правилом. Контейнер не может иметь состояния, а презентационный компонент наоборот.
  • Классы и Функции. Начиная с React 0.14, компоненты могут быть объявлены как классы или функции. Функциональные компоненты проще определить, но им не хватает некоторых функций доступных классов. Некоторые из них могут убрать в будущем, но сейчас они есть. Посколько функциональные компоненты легче понять, я советую использовать из пока вы не будете нуждаться в хранении состояния, участия в жизненном цикле или оптимизации производительности, которые доступны в данный момент для компонентов образованных из классов.
  • Чистый и Не чистый. Люди скажут, что компонент чистый если он гарантированно возвращает тот же самый результат для одинакового набора props и state. Чистые компоненты могут быть определены и как классы, и как функции, и могут иметь свое собственное состояние или нет. Другой важный аспект чистых компонентов в том, что они не опираются на глубокие мутации в state и props, поэтому их производительность может быть оптимизирована за счет использования shouldComponentUpdate(). Сейчас только классы могут определять shouldComponentUpdate(), но это может измениться в будущем.

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

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

Пример

Gist от Michael Chan.

Дополнительный материалы

Примечание

*В ранней версии статьи я называл их “умный” и “глупый” компоненты, но это было очень грубо и, самое главное, не объяснил разницу в их целях. Я нахожу новые термины более лучшими и, надеюсь, вы тоже!
**В ранней версии статьи я утверждал, что презентационные компоненты должны содержать только другие презентационные компоненты. Сейчас я не думаю так. Является компонент презентационным или контейнером зависит от реализации. Вы должны быть в состоянии заменить презентационный компонент с контейнером без изменения места вызова на сайте. Таким образом, презентационные компоненты и контейнеры могут отлично содержать в себе другие презентационные компоненты или контейнеры.

-Dan Abramov

29.01.2017 Эту страницу просмотрели за все время 9370 раз(а)


Twitter


Облако тегов