Если вы работаете с React или React Native и чувствуете, что у вас медленная скорость написания кода, вы тратите много времени на поиск ошибок, а не на добавление нового функционала, работаете с большими исходными файлами и вам сложно находить нужные части кода, а также многократно реализуете одну и ту же логику, то эта статья для вас. Вы удвоите скорость написания кода, если отрефакторите код в повторно используемые строительные блоки.
Мы рассмотрим React компонент, который можно хорошо разобрать. И мы будем шаг за шагом реорганизовывать его до хорошего уровня. Вы увидите большое количество идей, которые повысят вашу скорость написания кода.
С повторно используемыми компонентами и хуками легко создать что-то потрясающее. Это похоже на сборку частей Lego.
– Cory House. Проектирование повторно используемых компонентов React
Рефакторинг, рефакторинг и рефакторинг – это нормально; вам придётся менять свой код много-много раз; это естественный процесс обучения.
—Tomas Eglinskas. Самые важные уроки, которые я извлёк за год работы с React
В этой статье будет много кода на React. Не бойтесь кода. Не торопитесь, прочтите и поймите его.
Это большая статья. Содержание всеобъемлющее. Не стесняйтесь сохранить эту статью в своих вкладках и прочитать её несколько раз.
Даже несмотря на то, что код будет на React Native, вы можете легко использовать эту статью, чтобы улучшить свои навыки React.
Исходный компонент, который мы будем рефакторить
Простой компонент, написанный таким же разработчиком, как вы. Что он делает:
Получает список браузеров с сервера.
Показывает состояние «загрузки» на экране.
Показывает загруженный список в виде списка карточек.
Когда пользователь нажимает на карточку, появляется модальное окно с подробным описанием браузера.
Этот код похож на ваш?
Чем хорош этот компонент:
Он корректно работает: список будет загружен и показан пользователю со всеми описанным выше функционалом.
Он использует React Hooks.
Что плохого в этом компоненте
Этот компонент является монолитом, потому что он был спроектирован и написан для использования в одном месте в одном приложении только один раз.
Запомните эту мантру:
Искусство быстрого разработчика – это искусство писать повторно используемые строительные блоки.
Чем больше возможностей для повторного использования ваших компонентов и функций, тем вы быстрее. Это даёт вам огромное преимущество:
Вы тратите меньше времени на написание кода. Повторно используемые компоненты/функции могут скрывать тонны кода внутри себя и могут использоваться сотни раз. А реализовать их нужно будет только один раз. Представьте, что каждый раз, когда вам нужно обратиться к API, вы используете «fetch(URL)», а это замедляет вашу работу.
Вы тратите еще меньше времени на написание кода. Гораздо быстрее добавлять новые функции в одно место, чем добавлять их в 10 разных мест. Если вы повторно используете свои строительные блоки, у вас будет только одно место для добавления ваших функций. Реализуйте функцию в одном месте, а результаты получайте везде.
Вы тратите меньше времени на поиск ошибок. Вы будете использовать уже проверенные строительные блоки. Новый код нужно тестировать, но старому можно доверять.
Вы тратите меньше времени на чтение кода. Если вы повторно используете строительный блок, это сэкономит ваше время на понимание кода. Легче создавать программы из знакомых частей.
Вы тратите еще меньше времени на чтение кода. Отдельные повторно используемые строительные блоки можно использовать в разных файлах, поэтому вы будете работать с меньшими модулями и на вашем экране будет меньше кода. Также это улучшит понимание вашего кода.
Разработчик обычно тратит больше времени на выяснение того, что делает код, а не на его написание. – Alexis Mangin. Почему разработчики React должны разбивать свои приложения на модули?
Вы пишете меньше тестов для повторно используемых строительных блоков. Тесты делают вашу жизнь сладкой, как мёд, но с монолитом вы обнаружите, что снова и снова проверяете одну и ту же логику. Больше повторно используемых строительных блоков = меньше тестов.
Монолит = медленное написание кода.
Многоразовые строительные блоки = быстрое написание кода.
Как сделать эти 100 строк кода более пригодными для повторного использования
Давайте сначала попробуем что-нибудь действительно базовое и простое, а затем перейдём к более сложным вещам. Как мы можем распределить эти 100 строк в разные места?
Перенести константы в пропсы вашего компонента
Мы переместили константу URL-адреса в пропсы и уже можем повторно использовать компонент Browser с другим URL-адресом. Несомненно, небольшое влияние на повторное использование, но это очень легко реализовать.
Разогрелись? Тогда давай попробуем что-нибудь действительно крутое.
Разделите бизнес-логику и интерфейс
Жизнь проще, когда компоненты пользовательского интерфейса не знают о сети, бизнес-логике или состоянии приложения. Передавая одни и те же пропсы, всегда отображайте одни и те же данные. – Eric Elliott, Недостающее введение в React
Это самая важная часть статьи. Если вы можете выучить только одну вещь, пусть ею будет она:
Искусство быть быстрым React-разработчиком – это искусство отделения бизнес-логики от интерфейса.
Ух ты, какой всплеск сложности! Не бойтесь, я научу вас.
Бизнес-логика: логика, которая принимает решения и сохраняет состояние, например: всё, что находится в <Browsers /> выше ключевого слова return.
Интерфейс: всё, что отображает состояние на экране и считывает ввод пользователя: всё, что находится внутри return(…)
Давайте сделаем небольшой шаг вперёд и разделим наш компонент на 2 части, а затем посмотрим, как это сделает наш код более пригодным для повторного использования.
Что мы будем делать:
Создадим кастомный хук useBrowsers() для нашей бизнес-логики.
Создадим компонент <BrowsersList /> с множеством пропсов для нашего интерфейса.
Хорошо, наш код далёк от совершенства, но стал более пригодным для повторного использования:
Мы можем использовать <BrowsersList /> с другим источником данных. Раньше мы были обязаны использовать данные только с одного места, но теперь мы можем получить их из памяти, с диска или любого другого места.
Мы можем использовать useBrowsers() с другим экраном или даже без экрана. Например, мы можем использовать useBrowsers() в другом приложении с другим дизайном.
Но наш разделённый код далёк от совершенства! Мы всё ещё можем сделать этот код более пригодным для повторного использования.
Повторно используемые строительные блоки = быстрое написание кода.
Разделите свой код на множество небольших файлов
Перво-наперво. Разделение монолита на 2 части открыло новые возможности для рефакторинга. Мы можем сделать нашу программу более читаемой (а вас – быстрее), если разделим наш код на разные файлы/модули. Это позволит нам думать о каждом модуле изолированно,а также сделать каждый модуль более читабельным и многоразовым.
index.js экспортирует <Browsers /> с Browsers.jsx.
В папках «components» и «hooks» хранятся строительные блоки, относящиеся к компоненту <Browsers />.
BrowsersList.jsx также может превратиться в папку со связанными «хуками», «компонентами» и индексом.
Файловая структура проекта React представляет собой рекурсивное дерево.
Подробнее о структуре React-приложений читайте в статье автора David Gilbertson На 100 % правильный способ структурировать React-приложение (или почему такого способа нет).
Давайте посмотрим на наш BrowsersList.jsx.
Стало гораздо меньше кода. Он стал гораздо менее загромождённым. Теперь мы можем сосредоточиться на рефакторинге этой части кода.
Меньше беспорядка на экране = быстрое написание кода
Но, прежде чем мы сделаем этот компонент более пригодным для многоразового использования, позвольте мне затронуть большую проблему текущего дизайна.
Большая проблема с множеством небольших повторно используемых файлов
Посмотрите на наш компонент <BrowsersList />:
если мы переименуем некоторые пропсы, например changeDescription в setSelectedBrowser или description в browser,
или мы удалим некоторые пропсы,
или добавим некоторые пропсы,
В каждом месте, где используется <BrowsersList />, будет ошибка!
Каждый раз, когда вы меняете входящие данные компонента, вы ломаете каждое место своего кода, в котором используется этот компонент. И вы будете часто менять входные данные, так как сложно всё продумать в первый же день, например: дизайн часто возникает из того, как вы пишете код. А поскольку мы сосредоточены на создании множества компонентов, которые можно использовать повторно, во многих местах будут возникать ошибки.
Ваша IDE не может переименовывать пропсы с помощью автоматического рефакторинга. Вам нужно будет сделать это вручную. Вручную проверить каждое место использования. Это замедлит вашу работу.
И хуже всего то, что мы получим ошибку и не узнаем об этом. Единственный способ поймать её – это:
запустить приложение;
вручную перейти к сломанному компоненту и
получить ошибку;
прочитать текст ошибки;
исправить ошибку и попробовать ещё раз.
Это медленная и утомительная задача. Вот так неприятные баги попадают в продакшн. А такое бывает очень часто.
Проверьте, работает ли наш Browsers.jsx?
Кто знает! Для уверенности нам нужно открыть BrowsersList.jsx и useBrowsers.js и вручную сравнить входные данные.
Этот код не работает?
Да. У «descripton» в useBrowsers.js отсутствует «i», это опечатка.
Многие джуниор-разработчики часами отлаживают свои приложения и пытаются найти, какой пропс был сломан или какой пропс принимает неверные данные (строка вместо числа). Есть быстрое решение этой проблемы.
Перестаньте медленно писать код на JavaScript, ускорьте себя с помощью TypeScript!
Наступил 2021 год, и каждый разработчик React/React Native должен использовать TypeScript. Фактически нет причин не использовать его.
Он может выглядеть страшно.
Преимущества TypeScript значительно перевешивают недостатки, которые действительно существуют в JavaScript. Наиболее значимыми для нас являются дополнительные расходы на обучение.
Наиболее очевидное решение этой проблемы – просто включите TypeScript, не меняя код, добавьте несколько простых объявлений типов. В конце концов, наш опыт показывает, что при простейшем использовании TypeScript можно получить много преимуществ. – Felix Rieseberg, TypeScript в Slack.
Классные фичи TypeScript
Меньше ошибок.
Меньше ошибок = более быстрое написание кода.
38% ошибок в Airbnb можно было предотвратить с помощью TypeScript, согласно анализу.
TypeScript избавляет от ошибок.
Мы бы сразу нашли опечатку с буквой «i»:
Ваша IDE получит функцию автозаполнения для пропсов в React. Это особенно экономит ваше время, когда вы используете сторонние компоненты с неизвестными пропсами:
Ваша IDE получит функцию «рефакторинга». Это сэкономит вам время на переименование пропсов. Все использования пропса будут автоматически переименованы одним щелчком мыши.
Вы никогда больше не забудете добавить проверку на null/undefined.
И, конечно же, мы получим сообщение об ошибке, если попытаемся поместить недопустимые данные в наш пропс:
TypeScript легко сэкономит вам массу часов и избавит от стресса, связанного с отладкой кода.
JavaScript = медленное написание кода.
TypeScript = быстрое написание кода.
Первый шаг к быстрому проектированию системы: определите свои типы.
Это неполное руководство по TypeScript. Если вы чувствуете, что застряли, перейдите на typescriptlang.org, а затем вернитесь к статье.
Вернёмся к нашему компоненту <BrowsersList /> и определим некоторые типы для его пропсов (не забудьте переименовать файл с .jsx в .tsx).
И обновите сигнатуру хука useBrowsers():
Теперь TypeScript проверит, что useBrowsers() и BrowsersList совместимы. Если мы когда-нибудь изменим входные данные для BrowsersList, мы получим ошибку. Один этот факт гарантирует гораздо меньше ошибок в продакшне.
Меньше ошибок = более быстрое написание кода.
Быстрая системная архитектура
BrowsersListProps в настоящее время выглядит беспорядочно:
Компонент должен показывать состояние загрузки. Используется 1 строка в определении типа.
Должен отобразиться список Browser[]. Используется 1 строка в определении типа.
Должно появиться модальное окно с подробным описанием браузера. Используются 4 строки в определении типа. Эти строки ограничивают нас отображением поля «description» в нашем модальном окне. И кажется, что есть вероятность, что нам понадобится отобразить больше полей браузера в этом модальном окне. Помните, очень дорого менять сигнатуру компонента.
Мы можем сделать предположение и сократить определение типа и реорганизовать функцию «модального окна», чтобы использовать тип браузера.
Это снизит сложность пропсов:
Это небольшое упражнение по рефакторингу должно продемонстрировать вам еще одну замечательную особенность TypeScript – быстрое проектирование системы.
Типы писать очень легко. Их быстро описывать (4 строчки типов против 60 строк фактического компонента), а также они содержат много информации о вашей системе. Таким образом, вы можете написать несколько типов и разработать свою программу, фактически не написав никакого кода. Это сэкономит вам много-много времени на архитектуру.
Первый шаг в проектировании любой системы: определение ваших типов.
Архитектура и планирование с типами TypeScript = быстрое написание кода.
После того как вы определили свои типы, вы можете начать писать логику компонента
Давайте исправим <BrowsersList />, так как он будет обрабатывать новую сигнатуру BrowsersListProps. Мы можем провести рефакторинг <BrowserItem />, так чтобы потребовалось бы только 2 пропса вместо множества. Это сделает код более читаемым, а нас – быстрее.
Если у вашего компонента много пропсов, это хороший намёк на возможный рефакторинг.
Этот компонент уже выглядит более читабельным и менее пугающим.
Меньше беспорядка на экране = быстрое написание кода.
Извлечение повторно используемоей логики <UIFriendlyList/> от <BrowsersList/>
Просто наблюдая за компонентом, разработчик испытывает множество разных ощущений. Например, я чувствую, что «отображать состояние загрузки для списка» в <BrowsersList /> – это очень крутая функция. Настолько крутая, что, скорее всего, она будет использоваться в различных компонентах.
Это потенциально очень полезная и многоразовая функция нашего приложения. Но в настоящее время она связана с компонентом <BrowsersList />, и мы не можем повторно использовать её в другом месте. Давай исправим это.
Мы хотим создать новый компонент <UIFriendlyList /> и использовать его вместо <FlatList />. Этот <UIFriendlyList /> сможет отображать состояние загрузки.
Как всегда, мы начинаем с определения некоторых типов:
«T» – это аргумент типа. Типы с аргументами называются Дженерик. «T» для «UIFriendlyList <T>» – то же самое, что «arg» для функции «foo (arg)». Если вы хотите построить свой тип из другого типа, используйте Generic. Для получения дополнительной информации ознакомьтесь с этой статьёй Ross Bulat.
Объяснение дженериков в TypeScript
«&» – это пересечение. «Type X = A & B» означает, что X будет содержать A и B.
Посмотрите на это крутое решение:
Мы определяем типы для наших пропсов UIFriendlyListProps.
Мы определяем универсальный тип: список может содержать элементы любого типа.
UIFriendlyListProps расширяет FlatListProps из библиотеки React Native с помощью нашей функции «индикатора загрузки».
Итак, мы определяем тип UIFriendlyListProps как пересечение типов из FlatListProps и {loading ?: boolean}
Насколько это круто?
Я доволен этим дизайном, давайте напишем тело этого компонента и переместим его в другой файл UIFriendlyList.jsx
Мы также добавили пустое состояние. Таким образом, пользователь будет уверен, что список пуст и не будет ждать поступления данных. Очень дружелюбная функция пользовательского интерфейса.
<UIFriendlyList /> – это компонент, который можно использовать повторно, и он наверняка сэкономит нам время в будущем. С этим компонентом мы стали более быстрым React-разработчиком.
Теперь давайте проверим наш <BrowsersList />:
Вот о чём я говорил. Этот компонент намного проще понять по сравнению с исходным BrowsersList . И у нас есть переиспользуемый компонент <UIFriendlyList />, который наверняка сэкономит нам время. Мы можем пойти еще глубже и предположить, что мы хотим повторно использовать логику ModalWindow + List, но мы на этом остановимся.
Этот процесс «извлечения повторно используемых частей» бесконечен и требует времени. Вам следует остановиться, если вы чувствуете, что можете провести время более продуктивно в другом месте.
Мы, люди, склонны писать код как форму прокрастинации, откладывая решение сложных проблем, которые у нас есть сейчас, путём решения гипотетических проблем будущего. – Justin Travis Waith-Mair. Прекратите писать повторно используемые компоненты React.
Мы закончили с интерфейсом, пора проверить бизнес-логику в хуке useBrowsers().
Рефакторинг бизнес-логики в хуке useBrowsers()
Как и компоненты, мы можем создавать настраиваемые повторно используемые хуки. Это делает код более читаемым, потому что кодовая база разделена на более мелкие, многоразовые блоки. – Bikash Paneru. Как лучше писать функциональные компоненты в React.
Давайте проведем рефакторинг useBrowsers(), чтобы вернуть валидный объект BrowsersListProps. Я также отрефакторил «loading»: теперь пропс будет установлен в «true» перед запросом и на «false» – после.
Выглядит неплохо, но мы можем пойти дальше и сделать многоразовый строительный блок. Функционал «запросить данные и сохранить результат в стейт, пока отображается загрузка» выглядит как крутой многоразовый строительный блок. Мы хотим отделить его от useBrowsers().
Как всегда, давайте начнём с определения некоторых типов. Мы хотим сделать хук useFetch(), который сможет хранить полученные данные в стейте, а также включать индикатор загрузки. Мы также хотим определить форму данных, которые мы получаем от API, как FetchBrowsersResults:
Неплохо выглядит, теперь давайте определим тело useFetch() и переместим его в специальный файл useFetch.ts:
Я также добавил нотификации на случай ошибки запроса. Таким образом, пользователь увидит описание ошибки.
useFetch() – это многоразовая функция, которая наверняка сэкономит нам время в будущем. С помощью этой функции мы стали более быстрым React-разработчиком.
Теперь займёмся рефакторингом хука useBrowsers():
Сравните с изначальным useBrowsers(), он намного меньше по размеру и прост для понимания.
Я не вижу логики, которую можно было бы использовать повторно, из этого компонента. Итак, давайте двигаться дальше.
4 простых совета, как стать более быстрым React-разработчиком.
С основной частью мы закончили, давайте немного расслабимся. Я дам вам 4 простых совета.
1. Никогда не форматируйте код вручную.
Ваша IDE должна предоставлять вам функцию «автоматического рефакторинга кода». Ваш проект на React должен содержать файлы .eslintrc.js и .prettierrc.js. Они настраивают линтинг и стиль кода. Вы должны иметь возможность применять этот стиль, нажав горячую клавишу:
2. Никогда не импортируйте модули вручную.
Ваша IDE должна предоставлять вам функцию «автоматического импорта». Никогда не вводите «../../../» вручную и не тратьте время на ввод/удаление импорта вручную.
Попробуйте функцию «автоматического импорта» в действии:
3. Перемещайтесь по проекту как профессионал
Если вы строите свой проект из небольших повторно используемых частей, у вас будет много небольших файлов и компонентов, которые можно использовать повторно. Скоро вам станет ясно, что навигация с помощью «мыши и открытых вкладок» и «выбора файла в списке файлов» станет практически невозможной.
Посмотрите на эту огромную файловую структуру крошечного мобильного приложения:
Знаете ли вы, как мучительно пытаться найти нужный файл во вкладках?
Ваша IDE должна предоставлять вам следующие полезные инструменты:
перейти к файлу с помощью строки поиска;
перейти к компоненту с помощью строки поиска;
перейти к ранее открытому файлу;
перейти к определению компонента с помощью курсора;
перейти к использованию компонента.
Изучите горячие клавиши своей IDE. Это сделает вашу работу гладкой, как масло.
4. Используйте линтинг ESLint
Каждый React-разработчик знает о боли неправильной зависимости хуков useEffect/Memo/Callback. В них всегда сложно найти ошибки:
ESLint позволяет без труда кешировать эти ошибки.
Инструменты разработки, такие как eslint и typescript, помогают поддерживать кодовую базу в большой команде. Хороший разработчик умеет программировать. Хороший разработчик умеет работать в команде. – Roman Nguyen. Создание архитектуры вашего React приложения. Перспективы разработки и бизнеса, о чём следует знать.
Все эти функции доступны в IDE Webstorm. Я рекомендую вам использовать Webstorm для разработки на TypeScript с React.
Заключение: искусство разработки повторно используемых строительных блоков
Искусство быстрого разработчика – это искусство писать повторно используемые строительные блоки.
Чтобы быть быстрым, вам необходимо:
Отделить бизнес-логику от интерфейса.
Использовать TypeScript, чтобы получать меньше ошибок.
Использовать TypeScript для включения мощных функций IDE: линтинга, рефакторинга имен, автозаполнения.
Определять типы перед написанием кода для быстрого проектирования архитектуры.
Разделять свой код на множество небольших файлов многократного использования, чтобы уменьшить беспорядок.
С помощью IDE форматировать код и импортировать модули.
Переходить по файлам с помощью горячих клавиш.
Практика рефакторинга.
Мы успешно отрефакторили компонент <Browsers />. Посмотрите на исходный компонент: это огромный, сложный для понимания монолит. Такой код кажется тяжёлым и замедляет нас.
Взгляните на эту лёгкую, как пёрышко, красоту:
Мы сделали простой для понимания компонент + извлекли 2 очень удобные многоразовые детали:
useFetch();
<UIFriendlyList />.
Мы можем использовать этот функционал в разных местах нашего приложения и, возможно, даже в разных приложениях. Это наверняка ускорит процесс разработки.
Поэкспериментируйте с кодом из статьи
Посмотрите итоговый результат в Codesandbox, чтобы поэкспериментировать и узнать больше. Этот код написан на React, он немного отличается от кода React Native в статье.
Что дальше?
Много читать.
Много практиковаться.
Задавать вопросы. Особенно под этой статьёй в разделе комментариев.
И последнее, но самое важное:
Автоматически тестируйте компоненты React.
Практика TDD.
Начать тестировать ваш код сложно, но это сделает вашу жизнь сладкой, как леденец. Посмотрите статью Ian Wilson "Как создавать надёжные приложения React с TDD и библиотекой тестирования React", это хорошая точка для начала изучения тестирования. Ежедневно тренируйте свое ремесло и со временем вы станете мастером. А также не забывайте осваивать новое (например с нашей помощью) — только так можно сохранять свою актуальность в стремительно изменяющемся мире технологий. А наши менторы и чуткая поддержка в этом помогут.
Узнайте, как прокачаться в других специальностях или освоить их с нуля:
Другие профессии и курсы
ПРОФЕССИИ
КУРСЫ
Modin
И чё в этом плохого конкретно для этого компонента?
Ну давайте тогда все компоненты делать переиспользуемыми, с десятками пропсов и сотнями if else внутри, чтобы потом красноглазить по вечерам и фиксить там баги.
Grado
Тут идет речь про то, что ты можешь вынести определенный функционал и использовать его в других местах или проектах чтобы не тратить время на написание однотипного кода.
Modin
Тут речь о том, что надо писать ВСЕ компоненты так, чтобы их можно было переиспользовать много раз в коде, с чем я искренне не согласен. Если ваше приложение чуть сложнее чем каталог продукции или личный блог, этот путь развития никуда вас не приведёт.