Hitech logo

Кейсы

Организация React-компонентов для быстрой масштабируемости проектов

TODO:
Роман Савицкий12 июля 2022 г., 08:23

В мире существует 2 миллиарда публичных вебсайтов, не считая внутренних инструментов. Каждый день добавляется ещё 250 000 новых — или в среднем по 4 сайта в год на каждого существующего фронтенд-разработчика. Четырежды в год каждый из них запускает редактор кода в новом проекте и думает, что в этот раз уж точно всё будет под контролем.

Самые интересные технологические и научные новости выходят в нашем телеграм-канале Хайтек+. Подпишитесь, чтобы быть в курсе.

Нескольких страниц, пара запросов к API, простейший стейт — с этим может справиться любой профессионал. Потом ещё пара страниц, немного логики, пара новых хуков. Переключатель языка. Тёмная тема. Мобильная версия. И вот уже номера строк в файлах компонентов становятся четырёхзначными. Код усложняется, появляются ошибки. В этой статье Дмитрий Радковский, эксперт разработки 2web и 3web платформ, рассказал, как организовать React-компоненты так, чтобы проект мог легко пережить проблему бурного роста.

Зачем

В большинстве проектов в процессе развития возникают одни и те же потребности:

  • локализация на разные языки
  • редизайны и светлая/тёмная тема
  • мобильная версия
  • А/Б-тестирование внешнего вида
  • А/Б-тестирование UX
  • Для удовлетворения этих потребностей удобно работать с базой компонентов, разбитой по слоям, где каждый слой берёт на себя Single Responsibility и взаимодействует исключительно с интерфейсом нижнего слоя.

    С выходом React Hooks и React Contexts появилась возможность изолировать компоненты в независимые ветки дерева, не коммитясь на конкретную архитектуру приложения раньше времени.

    Условия

    Состояние (стейт) приложения

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

    Использование в легаси

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

    Эффективность

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

    Слои

    В этой статье я описываю разбиение на три слоя компонентов:

  • View
  • Container
  • Global
  • View-компоненты

    Компоненты уровня презентации («глупые» компоненты, View-компоненты).

    Признаки View-компонентов:

  • используют HTML-тэги (<div>, <span>, …)
  • используют CSS-стили или css-in-js, импортируют CSS-файлы
  • импортируют другие View-компоненты и используют их
  • получают все значения через пропсы
  • могут получать через пропсы конкретные параметры дизайна: размеры, цвета, стили
  • View-компоненты НЕ могут:

  • обращаться к глобальному стейту или сервисам
  • содержать локализуемый текст (надписи на кнопках, заголовки и т. п.)
  • импортировать другие компоненты, кроме View-уровня
  • иметь сложную логику или стейт
  • Примеры

    Простая кнопка с прокидыванием цвета через пропсы

    Простая кнопка определённого цвета

    Сложный составной экран без логики

    Подложка под попап без логики и содержимого

    Container-компоненты

    Компоненты уровня логики («умные» компоненты, Container-компоненты).

    Признаки Container-компонентов:

  • импортируют View-компоненты и используют их
  • импортируют другие Container-компоненты и используют их
  • получают через пропсы параметры, на основе которых строят дерево дочерних элементов
  • могут использовать if/else конструкции
  • могут оборачивать колбеки/хэндлеры и передавать в дочерние элементы
  • могут использовать React Context для получения текстов локализации или настроек цветовой темы
  • Container-компоненты НЕ могут:

  • использовать HTML-тэги (<div>, <span>, …) — вместо этого используются View-компоненты
  • использовать CSS-стили или css-in-js, импортировать CSS-файлы — вместо этого используются View-компоненты
  • получать через пропсы конкретные параметры дизайна: размеры, цвета, стили — вместо этого используются View-компоненты
  • обращаться к глобальному стейту или сервисам
  • импортировать другие компоненты, кроме Container- и View-уровней
  • Примеры

    Экран пэйвола с глобальной локализацией и форматированием числа

    Подгружающийся список

    Global-компоненты

    Компоненты глобального уровня («глобальные» компоненты, Global-компоненты).

    Признаки Global-компонентов:

  • обычно возвращают ровно один элемент Container-компонента, в который прокидывают значения из глобального стейта
  • могут обращаться к глобальному стейту
  • могут использовать useState
  • могут использовать useEffect
  • могут использовать useReducer или useTesm
  • импортируют Container-компоненты и используют их
  • могут использовать if/else и switch/case конструкции
  • оборачивают глобальные колбеки/хэндлеры и передают в дочерние элементы
  • могут использовать React Context для любых целей
  • Global-компоненты НЕ могут:

  • использовать HTML-тэги (<div>, <span>, …) — вместо этого используются View-компоненты
  • использовать CSS-стили или css-in-js, импортировать CSS-файлы — вместо этого используются View-компоненты
  • получать через пропсы конкретные параметры дизайна: размеры, цвета, стили — вместо этого используются View-компоненты
  • строить дерево из вложенных элементов нескольких уровней — это дерево строится в отдельном Container-компоненте
  • Примеры

    Окно оплаты с вызовом глобальной функции

    Главный экран приложения на основе авторизации юзера

    Заключение

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

    Распределение компонентов по слоям позволяет более точечно распределить ответственность внутри команды и поднять автономность:

  • верстальщики ограничиваются слоем View, не выходя на другие
  • UX-разработчики собирают страницы и экраны как конструктор из готовых View-компонентов внутри своих Container-компонентов
  • разработчики стейта становятся в некотором смысле бэкенд-разработчиками: они пишут логику стейта приложения, получение и обработку данных, но никогда не касаются непосредственно UX и внешнего вида приложения, только предоставляют доступ к необходимым данным для Global-компонентов
  • Такой подход уменьшает оверхед на коммуникацию в команде, что особенно важно в географически распределённых командах. Кроме того, снижается риск конфликтов в системе контроля версий, что тоже экономит время на решение конфликтов и мердж веток.