Проектирование архитектуры ПО React: лучшие практики

Компоненты

Используйте функциональные компоненты

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

Пишите согласованные компоненты

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

Присваивайте имена компонентам

Компонентам всегда нужно давать имена. Это поможет отследить ошибку с помощью React Dev Tools.

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

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

Не используйте жесткую разметку

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

Регулируйте длину компонента

Компонент в React  —  это просто функция, которая может принимать пропсы в качестве аргументов и возвращать разметку. При сложной функциональности делите большие компоненты на более мелкие. Используйте пропсы и функции обратного вызова, чтобы обеспечить взаимодействие компонентов и обмен данными между ними. Количество строк кода не имеет значение  —  важна логика.

Комментируйте JSX

Реализуя что-либо важное, пишите комментарии в JSX. Если в разметке есть логика, важно внести ясность в виде комментариев.

Сокращайте количество пропсов

Как уже отмечено выше, компоненты React  —  это функции. Они используются для получения пропсов и возврата разметки, поэтому использовать пропсы везде нецелесообразно.

Сколько пропсов должен иметь компонент? Ответ в данном случае прост: чем больше пропсов компонент получает, тем больше у него причин для повторного рендеринга. Компонент должен иметь минимум пропсов.

Отдавайте предпочтение объектам, а не примитивам

Чтобы ограничить количество пропсов, используйте объекты вместо значений примитивов. Общий совет: используйте TypeScript.

Используйте меньше вложенных тернарных операторов

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

Используйте отдельный компонент для списков

Циклический просмотр элементов списка  —  обычное явление. Для этого используется функция Map. Но если компонент содержит слишком много разметки, эта функция ухудшает читаемость. Если разметка длинная или сложная, используйте одно сопоставление (single mapping) для каждого компонента.

При деструктурировании назначайте пропсы по умолчанию

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

Не используйте вложенные функции рендеринга

Когда нужно использовать разметку из компонента, не определяйте ее в находящейся там же функции. Создайте другой компонент, переместите ее туда с соответствующим названием и полагайтесь на пропсы вместо функции-закрытия (closure).

Управление состоянием

Используйте редьюсеры

Иногда требуется более надежный способ для управления изменениями состояния. Прежде чем использовать внешнюю библиотеку, попробуйте useReducer. Это отличное решение для сложных задач управления состоянием, не требующее сторонних продуктов. useReducer может быть очень полезным в сочетании с React Context и TypeScript.

Используйте хуки для HOC (Higher-Order Component) и Render Prop

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

1. компонент высшего порядка;

2. Render-пропсы;

3. хуки.

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

Так мы получаем значения в виде пропсов с HOC. Из-за этого становится сложно определить, откуда исходят значения  —  от родительского компонента или от оболочки.

Render-пропсы могут осложнять читаемость кода. Если вложить несколько компонентов с рендер-пропсами, разметка будет очень сложной.

Используйте библиотеки выборки данных

Часто данные, которыми мы хотим управлять в состоянии, поступают из разных API, где их нужно хранить в памяти, обновлять и получать к ним доступ. Современная библиотека выборки данных React Query имеет целый ряд механизмов для управления внешними данными. Одни данные можно кэшировать и аннулировать, а новые извлекать. Если сравнивать React Query и GraphQL с клиентом Apollo, то GraphQL выглядит предпочтительнее, потому что он проще в использовании.

Применяйте библиотеки управления состоянием

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

Компоненты ментальной модели

Smart и Dumb

Все компоненты принято делить на две категории: Smart и Dumb (умные и глупые). Они также известны как контейнер и презентация.

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

Наличие и отсутствие состояния

Компоненты можно разделить на имеющие и не имеющие информацию о состоянии. Как уже отмечалось, некоторые компоненты сложны в управлении, поэтому их нужно распределить по всему приложению.

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

Например, данные формы обрабатываются компонентом <Form />. Когда произойдут изменения, <Input /> будет получать значения и вызывать функции. Когда пользователь нажмет <Button />, форма получит уведомление и начнет обработку события.

А как будет выполняться валидация Form? Позаботится ли об этом ввод? Это будет означать, что компонент узнает о бизнес-обосновании приложения.

А как форма будет уведомлена о возникновении ошибки? Как будет обновлено состояние на основе этой ошибки? Что произойдет в случае ошибки, когда пользователь попытается отправить форму?

Сталкиваясь с подобными проблемами, следует понимать, что разделение обязанностей все более осложняется. В этом случае не нужно давать какое-либо состояние для ввода, и форма выдаст сообщение об ошибке.

Структура приложения

Совмещайте маршрут и модуль (Route/Module)

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

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

Совмещайте Route/Module с самого начала. Такая композиция упрощает изменение и расширение проекта. Идея состоит в том, чтобы исключить быстрое разветвление архитектуры. Это произойдет слишком быстро, если приложение будет основано на компонентах и контейнерах.

К тому же архитектура на основе Route/Module легко поддается расширению. Нужно ясно понимать, что компонентно-контейнерная архитектура вполне допустима, но она излишне обобщенная.

Создавайте модуль Common

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

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

Используйте абсолютный путь

Изменения в структуре проекта нужно максимально упростить. Абсолютные пути потребуют меньше изменений при перемещении компонента.

Оборачивайте внешние компоненты

Не следует напрямую импортировать слишком много сторонних компонентов. Вместо этого лучше разработать для них адаптер. Это можно применить, например, к библиотекам пользовательского интерфейса, таким как BootStrap и Material UI. Самый простой способ  —  реэкспортировать их из модуля common.

Помещайте компоненты в папки

В приложениях React можно создавать папку components для каждого модуля. Для дополнительных файлов, таких как тесты и стили, можно создать отдельную папку.

Производительность

Не пытайтесь преждевременно оптимизировать производительность

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

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

Следите за размером пакета приложения

Объем кода JavaScript, отправляемого в браузер,  —  самый важный фактор, который может повлиять на приложение. Не отправляйте JavaScript в одном пакете. Делите приложение на уровне маршрута. Старайтесь отправлять в браузер минимум JavaScript.

Тестирование

Не полагайтесь на снэпшот-тесты

Удивительно, но они редко помогают находить ошибки в компонентах.

Тестируйте корректность рендеринга

Тесты в первую очередь должны подтвердить корректную работу компонента. Нужно убедиться, что рендеринг компонента с пропсами по умолчанию выполняется правильно.

Проводите интеграционные тесты кода

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

Стилизация

Используйте CSS в JS

Сторонние библиотеки, такие как Styled Components и Emotion, позволяют писать CSS в JS, не заботясь о соглашениях CSS. Возможно, не все с этим согласятся, но, по моему мнению, нет ничего плохого в том, чтобы использовать модули SCSS и CSS.

Поддерживайте Styled Components совместно

В одном и том же файле могут находиться несколько компонентов JS с CSS. Желательно, чтобы они находились в том же файле, что и обычный компонент, который их использует. Но если они станут слишком большими (это может случиться со стилями), перенесите их в собственный файл рядом с компонентом, который их использует.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Imran Farooq: React Best Practices for Software Design and Architecture

Предыдущая статьяКак быстро создать PDF-файл с помощью Python
Следующая статьяМагия совместимости XML и Jetpack Compose