Разработчик делится своим пятилетним опытом использования React: читаем и учимся на чужих ошибках.
Ознакомься с новым проектом
- Просмотри package.json, изучи зависимости, скрипты и конфигурацию.
- Нарисуй дерево проекта на бумажке/доске или используй React dev tools. Это помогает визуализировать состояние.
Процесс разработки
- Быстрый переход к компоненту или функции: клик с зажатой клавишей CMD прямо в JSX (VSCode).
- Быстрый переход к родителю: CMD+SHIFT+F (глобальный поиск по проекту в VSCode). Искать <Название_компонента.
- Быстрый просмотр списка родителей: React dev tools.
- Создавай чеклист возможных состояний каждого компонента (ошибка, нет данных и т. д. — полный список).
- Для дебаггинга используй debugger. Также полезен метод console.assert.
- Работай с фиктивными данными и фиктивным API (json-server, json-graphql-server).
- Используй одни и те же фиктивные данные для Storybook, тестов и фиктивного API.
- Передавай в функцию объект и используй деструктурирование для получения именованных параметров. Это читается гораздо проще. Деструктурировать можно прямо в сигнатуре функции, это позволяет сразу же документировать ожидаемые параметры.
- Storybook driven development — создавай и тестируй каждый компонент изолированно. Каждое состояние документа — это отдельная история. Затем используй Percy или Chromatic для снэпшотов
- Отступы
- Адаптивный дизайн
JSX
- Можно использовать только выражения, которые что-то возвращают. Для сложной логики есть несколько вариантов:
- Ранний return (удобно для лоадеров и ошибок)
- Выделение логики рендера в отдельную функцию, где можно использовать все возможности Javascript (if/else/switch)
Производительность
- Делай то, что удобно. Чаще всего это достаточно хорошо работает. Инлайн-функции? Отлично. Беспокоишься о рендере? Не беспокойся. Беспокоишься о производительности контекста? Может быть, ты неправильно используешь контекст (он должен меняться довольно редко). В любом случае тестируй, но не сходи с ума. Добиваясь идеальной производительности, ты потеряешь удобство.
- Помни, что рендер компонента — не равно изменению DOM-дерева. React работает с виртуальным DOM. Процесс применения изменений выглядит так: рендер (render) -> согласование (reconcile) -> применение (commit). Если DOM не изменяется, то и проблем с производительностью не будет, поэтому перестань волноваться о перерендерах. React достаточно умный, чтобы изменять только то, что требует изменения.
- Не используй везде, где можно, useMemo, shouldComponentUpdate, PureComponent. Только если это действительно нужно. У них есть накладные расходы, так как совершаются дополнительные действия. Если бы они были быстрыми, то использовались бы по умолчанию!
Управление состоянием
- Держи состояние так низко, как это только возможно. При необходимости поднимай.
- Избегай хранить состояние, которое может быть извлечено из другого состояния или пропсов. Вычисляй на лету. Ссылайся на объекты по идентификатору вместо того, чтобы дублировать их.
- Используй _myVar конвенцию для разрешения конфликтов именования состояния.
- Не синхронизируй состояния, а извлекай. Например, получай полное имя (full name) путем объединения имени (firstName) и фамилии (lastName) прямо в методе render.
- Состояния, которые изменяются вместе, должны храниться вместе. В этом помогают редьюсеры. Группируй, используй useState. Рассмотрим движки состояний — они описывают валидные состояния, что делает невалидные состояния невозможными (например, новый клиент, у которого уже есть 5 прошлых покупок, или админ без прав) Если разделить состояния, они могут рассинхронизироваться.
- Возможно тебе не нужен Redux. Подъем состояния отлично масштабируется и легок для понимания. Пробрасывание пропсов не так страшно, как его малюют. Просто сохраняй названия пропсов, используй спред-оператор, передавай детей целиком, используй мемоизацию. Контекст и useReducer могут справиться практически с любой задачей.
- Контекст полезен не только для хранения глобальных данных. Его можно использовать для композиции компонентов. Он также может быть полезен для увеличения производительности.
- Вызывай setLoading(false) в блоке finally.
Пропсы
- Сделай все пропсы обязательными.
- Деструктурируй пропсы в сигнатуре функции, чтобы уменьшить количество кода. Это также полезно для обработчиков событий. Но что насчет пропсов, в имени которых есть дефис типа aria-label. Просто используй синтаксис …otherProps.
- Сделай пропсы максимально специфичными.
- Используй стандартизированное наименование. onX для пропсов-обработчиков событий, handleX — для функций.
- Храни propTypes в одном месте.
- Документируй propTypes с помощью JSDoc-комментариев, они будут выводиться в автокомплите редактора. Можно даже использовать markdown.
- Чтобы уменьшить проблему пробрасывания пропсов, используй спред-оператор или передавай дочерние элементы.
- Само существование пропа предполагает, что он равен true. Поэтому достаточно написать <Input required />.
- Держись ближе к нативному API. В обработчики передавай событие целиком, а не только значение инпута. Тогда ты сможешь использовать единый обработчик изменений. Придерживайся нативных имен (onBlur, onChange). Стремись к максимальной гибкости и минимальной кривой обучения.
Стили
- Сочетай разные подходы:
- Инлайновые стили для динамически изменяющихся свойств.
- Пространства имен (CSS-модули).
- Sass для глобальных стилей.
- Используй классы, чтобы применять несколько стилей сразу.
- Используй flexbox или CSS Grid вместо float.
- Создавай абстракции для брейкпоинтов (например, используй bootstrap).
Переиспользование компонентов
- 3 ключевых правила для простого переиспользования.
- Подумай о том, чтобы выделить на это отдельного человека/команду. Зачем это нужно? Скорость работы, быстрее и проще принимать решения, меньше бандлы. Слаженность в интерфейсе ведет к хорошему пользовательскому опыту. Меньше кода — меньше багов.
- Ищи повторящийся код — его можно повторно использовать. Любое переиспользование — это улучшение производительности.
- Следуй принципу DRY, объединяй возможности хуков, контекста и переиспользуемые компоненты, чтобы инкапсулировать всю бизнес-логику приложения.
- Принимай и простые строки и элементы. Используй React.isValidElement, чтобы определить, что ты получаешь.
- Создай проп as для обозначения элементов высокого уровня.
- Cоздай переиспользуемый AppLayout, используя слоты.
- Помести алерты в одно место в AppLayout и предоставь функцию для их отображения через контекст.
- Генерируй документацию через react-docgen.
- Подумай о создании отдельных компонентов для мобильной и десктопной версии (если он сильно различаются). Используй lazy load для загрузки нужного размера.
Тестирование
- RTL лучше Enzyme. Чем проще API, тем лучше. Поощряет a11y. Проще отлаживать. Можно использовать те же запросы для Cypress.
- JSDOM не рендерит, поэтому не может тестировать адаптивный дизайн. Используй Cypress для теста адаптивного поведения.
- Избегай снэпшот-тестов Jest. Они хрупкие, тестирую детали реализации, плохо называются, всегда падают при изменении единственной строки и их трудно поднять. Вместо этого используй Percy или Chromatic для визуальных тестов.
- Используй шаблон выбора сценариев для запуска приложения с различными данными. Автоматизировать эти тесты можно с помощью Cypress/Selenium.
- При использовании Cypress Cy-селекторы совпадают с RTL-селекторам — не нужно менять код для поддержки тестов Cypress.
- Cypress driven development — TDD для интеграционных тестов. Используй Cypress, чтобы перейти к месту, которое нужно проверить. Используй cy.only чтобы запустить один тест. В первый раз он должен упасть, это пройдет.
Среда разработки
- Кастомизируй create-react-app (CRA):
- Используй react-app-rewired для настройки конфигурации без ejecting.
- Кастомизируй правила линтинга.
- Добавь webpack-bundle-analyzer, узнай, что в твоем бандле.
- Форкни CRA или хотя бы react scripts. Создай фреймворк для компании, который генерирует проект с единственной зависимостью — этим форком, включающим компоненты, правила линтинга, конфиги и т. д.
- Используй Prettier.
- Положись на ESLint. Используй как учебное пособие. Object shorthand. No var. Запрет некоторых импортов (jquery, lodash, moment). Строгое равенство. Добавьте плагины типа jsx-a11y/recommended.
- Используй строгие propTypes (или TS).
- Используй .vsextensions.
- Разделяй клиент и сервер. Если используешь React на сервере, используй Storybook для изолированной разработки компонентов.