Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Современный фронтенд крепко стоит на компонентах, потому что это действительно разумный и приятный подход к разработке. Собирать программу из «кирпичиков Lego» существенно проще и надежнее, чем с нуля писать монолит.

Так бросим же все силы на то, чтобы научиться мыслить в компонентном стиле!

Учимся готовить компоненты

Казалось бы, каждый джун сегодня умеет писать компоненты, чему тут учиться? Но компонент компоненту рознь, для наших программ нужны только самые качественные и красивые из них. Поэтому сейчас мы возьмем неограненный React-компонент, который написал простой разработчик вроде вас, и рассмотрим его очень пристально со всех сторон. А затем много-много раз отрефакторим, пока не получится идеальный бриллиант.

Будьте готовы к многочисленным озарениям и головокружительному увеличению вашей производительности!

Исходный компонент

Рассмотрим неограненный алмаз, с которым мы будем работать. Этот простой компонент умеет не так уж и много:

  1. он получает с бэкенда список браузеров;
  2. отображает индикатор загрузки, пока запрос выполняется;
  3. выводит полученные данные в виде карточек;
  4. если пользователь нажимает на карточку, компонент показывает модальное окно с детальной информацией о браузере.

Этот код похож на ваш?

Что с этим компонентом так?

Что с этим компонентом не так?

К чему мы стремимся?

Чем больше у вас разных кирпичиков, тем быстрее вы можете построить дом.

  • Не нужно повторять одно и то же по десять раз – помним о принципе DRY !
  • Не нужно вносить изменения в нескольких местах – и лихорадочно вспоминать, где еще есть этот функционал.
  • Не нужно ловить баги по всему приложению – они смирно сидят в компоненте и ждут вас.
  • Не нужно много читать, код сразу становится короче, чище и понятнее.
  • Не нужно писать тесты для каждого места, в котором используется функциональность.

Приступаем к огранке

Начнем с самых простых приемов рефакторинга, а затем войдем во вкус и сдеаем что-то посложнее.

Шаг #1. Перенос констант в пропсы

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

Шаг #2. Разделение бизнес-логики и отображения

Вероятно, это самая важная фундаментальная установка хорошего разработчика – разделение бизнес-логики от логики отображения. Жизнь становится проще, когда UI компоненты ничего не знают о серверах, логике и состоянии приложения .

В чем вообще разница?

Бизнес-логика: Код, который принимает решения и изменяет состояния. Все внутри <Browsers/> до инструкции return .

Логика отображения: код, который отображает состояние приложения на экране и взаимодействует с пользователем. Все внутри инструкции return в нашем примере.

Давайте произведем радикальное рассечение компонента на две части и убедимся, что жизнь действительно стала проще:

  • Пользовательский хук useBrowsers() с бизнес-логикой.
  • Компонент <BrowsersList /> с логикой отображения.

Код все еще далек от идеала, но уже стал немного более удобным.

  • <BrowsersList /> теперь можно использовать с разными источниками данных (а не только с данными из HTTP-эндпоинтов).
  • useBrowsers() теперь может работать с разными представлениями или даже вообще без представления. Если вы измените дизайн приложения, этот хук продолжит работать.

Шаг #3. Разделение на файлы

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

Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

  1. index.js экспортирует <Browsers/> из Browsers.jsx;
  2. строительные блоки для <Browsers/> лежат в папках components и hooks;

BrowsersList.jsx можно развить до самостоятельной папки с собственными хуками, компонентами и файлом index.js

Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Посмотрим на BrowsersList.jsx:

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

Большая проблема маленьких файлов

Взгляните на сигнатуру функции <BrowsersList /> . Что произойдет, если мы переименуем какой-нибудь пропс, например, changeDescription на setSelectedBrowser ? Или удалим аргумент? Или добавим новый?

Все сломается!

Каждый раз когда вы меняете сигнатуру компонента, возникает проблема во всех местах, где он используется. А вы не раз будете менять эту несчастную сигнатуру, потому что с первого раза редко получается хорошо. Ваша IDE это не отследит, придется ручками искать все вызовы и вносить правки. Это медленно и совсем не круто. Даже более: искать теперь придется по разным файлам, связь между которыми не всегда очевидна.

Ну и где профит-то, спросите вы? Опять что-нибудь где-нибудь забудем исправить – и прощай продакшн.

Посмотрите внимательно, все ли тут в порядке?

Бинго! Одна маленькая опечатка – и все идет кувырком.

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

Хватит медленно кодить на JS, давайте кодить быстро на TS

В 2021 году у вас нет оправданий, если вы не используете TypeScript. Вам даже не требуется сразу же изучить все его возможности, ведь можно просто добавить TS в проект, не меняя ни строчки , и постепенно наращивать его применение.

38% багов на Airbnb можно было предотвратить с помощью TypeScript38% багов на Airbnb можно было предотвратить с помощью TypeScript

С TypeScript ваша IDE обогатится несколькими крутыми фичами:

  • Автодополнение для пропсов
  • Возможность автоматического переименования пропсов
  • Проверка на null/undefined
  • Валидация значений пропсов

А вот, кстати, и наша опечатка:

Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Шаг #4. Определение типов

Так как это не полноценный гайд по TypeScript, разбираться в основах мы сейчас не будем. Все желающие могут заглянуть на typescriptlang.org (или прочитать статью в «Библиотеке программиста» – прим. ред.). Вернемся к <BrowsersList /> и преобразуем его пропсы в симпатичные типы (не забудьте сменить расширение с .jsx на .tsx).

Теперь обновим сигнатуру useBrowsers() :

И теперь TypeScript будет строго следить, чтобы useBrowsers() и BrowsersList были совместимы.

Быстрая разработка архитектуры

BrowsersListProps выглядит весьма нагруженно:

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

Мы можем уменьшить сложность этого фрагмента и отрефакторить функциональность модального окна для использования типа Browser :

TypeScript указывает на некорректность сигнатуры при ее измененииTypeScript указывает на некорректность сигнатуры при ее изменении

Этот маленький рефакторинг должен продемонстрировать вам замечательную способность TypeScript: быстро проектировать системный дизайн. Писать типы очень просто. Они маленькие, но содержат много полезной информации о вашей системе. Фактически вы можете описать вашу программу без кода, одними типами.

Теперь поправим <BrowserList /> , чтобы он мог работать с новой сигнатурой BrowsersListProps .

Отрефакторим <BrowserItem /> . Сейчас он принимает множество пропсов, и это явный сигнал для внесения правок. Теперь он будет принимать всего 2 аргумента:

Компонент стал проще и надежнее.

Шаг #5. Извлечение <UIFriendlyList />

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

Сейчас эта функциональность интегрирорована в <BrowsersList /> . Значит надо ее извлечь. Создадим новый компонент <UIFriendlyList /> и будем использовать его вместо простого <FlatList/ > .

Как всегда начнем с определения типов:

  • T – это аргумент типа, или дженерик. То же самое, что arg в сигнатуре foo(arg) . Дженерики нужны, если вы хотите создать свой тип из другого типа. Вот здесь есть подробнейшее описание дженериков.
  • & – это символ пересечения множеств (интерсекция). Тип X = A & B означает, что X содержит в себе свойства A и B .

Итак, что мы сделали:

  • Определили тип для пропсов нового компонента.
  • Использовали дженерик, чтобы указать, что список может содержать элементы разных типов.
  • UIFriendlyListProps расширяет встроенные класс FlatListProps из библиотеки React Native и добавляет в него состояние загрузки.

Теперь вынесем новый компонент в отдельный файл UIFriendlyList.jsx:

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

Компонент <UIFriendlyList /> – это крутой строительный блок, который очень легко переиспользовать в самых разных ситуациях. Добавьте его в свою коллекцию кирпичиков, чтобы стать еще быстрее.

Во что теперь превратился наш <BrowsersList /> :

Сравните его с исходным вариантом . Он почти в два раза меньше и гораздо проще для понимания. К тому же в качестве бонуса вы получили отличный компонент <UIFriendlyList /> , который в будущем сэкономит вам кучу времени. Можно пойти еще дальше и выделить, например, в отдельный кирпичик логику Модальное Окно со Списком, но давайте пока остановимся.

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

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

Шаг #6. Рефакторинг useBrowsers()

Хук useBrowswers() должен возвращать валидный объект с типом BrowsersListProps:

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

Как всегда, сначала типы. Нам нужно, чтобы useFetch сохранял данные из эндпоинта и отображал состояние загрузки. Формат данных будет задан отдельно, для этого используем дженерик:

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

Теперь сама функция:

Для полноты картины добавили Alert при ошибке запроса.

Теперь окончательно отрефакторим useBrowsers() :

Кажется, здесь больше нечего извлекать 🙂

4 простых совета для ускорения разработки

1. Никогда не форматируйте код вручную.

Используйте возможности IDE, ESLint и Prettier.

2. Никогда не импортируйте модули вручную

Использование автоматического импорта сэкономит много времени.

3. Научитесь ориентироваться среди множества файлов

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

Привыкните использовать возможности IDE: поисковую строку для поиска компонентов, горячие клавиши для перемещения по истории файлов и всплывающие определения, которые появляются при наведении курсора на название компонентов.

4. Используйте линтинг

Это поможет найти самые хитрые и вредные ошибки:

Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Искусство быстрой разработки

Чтобы быть быстрым, не потеряв при этом в качестве, вам нужно освоить несколько вещей:

  • Отделять бизнес-логику от отображения.
  • Разделять код на маленькие реюзабельные компоненты.
  • Использовать TypeScript и начинать работу с определения типов.
  • Практиковаться в рефакторинге.
  • Использовать плюшки вашей IDE.

Насколько вы постигли дзен быстрой разработки? Признавайтесь, часто рефакторите?