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

Компонент Ответственность

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

  • Управлять внутренним состоянием
  • Предоставление методов жизненного цикла компонентов
  • Подключитесь к хранилищу Redux (или аналогичной библиотеке управления состоянием)
  • Рендер jsx

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

Это адаптация принципа единой ответственности.

Если вы еще этого не сделали, прочитайте эту статью Дэна Абромова о компонентах Presentational и Container. Он делает фантастическую и краткую работу по объяснению этих концепций лучше, чем я когда-либо мог :)

Большинство идей в этой статье — это всего лишь итерации этих фундаментальных идей, представленных Дэном.

Стоит отметить, что хотя я могу упомянуть Redux в этой статье, представленные здесь принципы не относятся к какой-либо одной библиотеке управления состоянием. Эти идеи можно адаптировать для MobX, Apollo Client или просто Context API. Я всегда думал, что самая мощная часть Redux — это не сама библиотека, а идея разделения задач вашего приложения на простые компоненты.

Слои компонентов

Я очень не люблю классовые компоненты в React. Основными причинами выбора компонента класса вместо функционального компонента являются (1) управление внутренним состоянием или (2) доступ к методам жизненного цикла компонента.

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

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

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

Допустим, вы создаете поле ввода формы. Если вы следуете принципу единой ответственности компонентов, вы можете получить иерархию компонентов, показанную здесь:

...
  Input
    InputContainer
      InputWithState
        InputPresenter
          ... 
  • InputPresenter— Рендерим JSX и отвечаем на пользовательские события
  • InputWithState — Используйте Recompose withStateHandlers для предоставления локального состояния и методов обновления состояния.
  • InputContainer— Подключитесь к магазину Redux и предоставьте создателям действий Redux
  • Input — Компонент-оболочка для более стабильного тестирования презентационных компонентов более высокого уровня.

Структура файла

К счастью для нас, создатели React наконец-то определились с лучшей файловой структурой для реактивных проектов!

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

Мне нравится организовывать файловую структуру моего компонента в соответствии с пользовательским интерфейсом приложения. Например:

components 
│
└───InputParent
│   └── InputParentPresenter.js
│   └── ...
│   │
│   └── children
│       │   
│       └── Input
│           └── index.js
│           └── InputContainer.js
│           └── InputWithState.js
│           └── InputPresenter.js
│           │
│           └── tests
│           │   └── InputContainer.spec.js
│           │   └── InputWithState.spec.js
│           │   └── InputPresenter.spec.js
│           │
│           └── children
│               │
...             └── ...

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

У этого подхода есть несколько недостатков, но есть простые обходные пути:

  • Сложно поделиться компонентами → Создайте общую папку в папке /components
  • Импорт в глубоко вложенные компоненты → Используйте псевдонимы webpack для часто используемых папок

Чтобы упростить создание этих компонентов, я сделал генератор строительных лесов Yeoman под названием mnml-component.

Вывод

Следование этому подходу упростило мне разработку и масштабирование приложений React. Пожалуйста, дайте мне знать в комментариях, если у вас есть какие-либо отзывы об этом подходе или инструменте mnml-component!