Начало работы с React
React использует синтаксис HTML-in-JavaScript под названием JSX (JavaScript и XML). Знание HTML и JavaScript поможет вам изучить JSX и лучше определить, связаны ли ошибки в вашем приложении с JavaScript или с более специфической областью React.
Настроить локальную среду разработки React, создать стартовое приложение и понять основы его работы.
Привет React
Для создания веб-приложений разработчики используют React в тандеме с ReactDOM. React and ReactDOM часто обсуждаются в том же пространстве и используются для решения тех же проблем, что и другие настоящие фреймворки для веб-разработки. Когда мы ссылаемся на React как на «фреймворк», мы подразумеваем это разговорное понимание.
Когда использовать
В отличие от других платформ, рассматриваемых в этом модуле, React не обязывает к строгим правилам в отношении соглашений о коде или организации файлов. Это позволяет командам договариваться, что для них более подходит, и структурировать React проект соответствующим образом. React может отвечать за одну кнопку, несколько частей или же весь пользовательский интерфейс приложения.
Хотя React можно использовать для небольших частей интерфейса, «зайти» в него не так просто, как, к примеру, в jQuery, или даже во Vue. Куда легче это сделать создав всё приложение с помощью React.
Кроме того, такие преимущества React-приложения, как написание интерфейсов с помощью JSX, требуют процесса компиляции. Добавление на сайт компилятора Babel приводит к более медленному выполнению кода, поэтому такие инструменты обычно настраиваются для процесса сборки. Да, возможно, у React есть серьёзные требования к инструментарию, но его можно освоить.
В этой статье основное внимание будет уделено использованию React для создания всего пользовательского интерфейса с помощью create-react-app, предоставляемого Facebook.
Как React использует JavaScript?
React utilizes features of modern JavaScript for many of its patterns. Its biggest departure from JavaScript comes with the use of JSX syntax. JSX extends JavaScript’s syntax so that HTML-like code can live alongside it. For example:
This heading constant is known as a JSX expression. React can use it to render that
Suppose we wanted to wrap our heading in a tag, for semantic reasons? The JSX approach allows us to nest our elements within each other, just like we do with HTML:
Note: The parentheses in the previous snippet aren’t unique to JSX, and don’t have any effect on your application. They’re a signal to you (and your computer) that the multiple lines of code inside are part of the same expression. You could just as well write the header expression like this:
However, this looks kind of awkward, because the tag that starts the expression is not indented to the same position as its corresponding closing tag.
Of course, your browser can’t read JSX without help. When compiled (using a tool like Babel or Parcel), our header expression would look like this:
It’s possible to skip the compilation step and use React.createElement() to write your UI yourself. In doing this, however, you lose the declarative benefit of JSX, and your code becomes harder to read. Compilation is an extra step in the development process, but many developers in the React community think that the readability of JSX is worthwhile. Plus, popular tooling makes JSX-to-JavaScript compilation part of its setup process. You don’t have to configure compilation yourself unless you want to.
Because JSX is a blend of HTML and JavaScript, some developers find it intuitive. Others say that its blended nature makes it confusing. Once you’re comfortable with it, however, it will allow you build user interfaces more quickly and intuitively, and allow others to better understand your code base at a glance.
To read more about JSX, check out the React team’s JSX In Depth article.
Настройка вашего первого React приложения
There are many ways to use React, but we’re going to use the command-line interface (CLI) tool create-react-app, as mentioned earlier, which expedites the process of developing a React application by installing some packages and creating some files for you, handling the tooling described above.
Основы React: всё, что нужно знать для начала работы
Хотите узнать о том, что такое React, но вам всё никак не выпадает шанс изучить его? Или, может быть, вы уже пробовали освоить React, но не смогли толком понять? А может, вы разобрались с основами, но хотите привести в порядок знания? Эта статья написана специально для тех, кто положительно ответил хотя бы на один из этих вопросов. Сегодня мы создадим простой музыкальный проигрыватель, раскрывая основные концепции React по мере продвижения к цели.
Разобравшись с этим материалом, вы освоите следующее:
Предварительная подготовка
Рассмотрим такую ситуацию: к вам за помощью обращается маленький стартап. Они создали приятную страницу, пользуясь которой пользователи могут загружать в их сервис музыку и проигрывать её. Им хочется, чтобы вы сделали самое сложное — вдохнули в эту страницу жизнь.
Для начала создайте новую директорию проекта и добавьте туда три файла. Вот они на GitHub, а вот их код.
Для успешного прохождения этого руководства вам понадобится свежая версия браузера Google Chrome, иначе не будут работать анимации. Выражаем благодарность Стивену Фабре за CSS для кнопки проигрывания и Джастину Виндлу за код визуализации (оригинал можно посмотреть здесь).
Откройте index.html в редакторе кода и в браузере. Пришло время познакомиться с React.
Что такое React?
React — это инструмент для создания пользовательских интерфейсов. Его главная задача — обеспечение вывода на экран того, что можно видеть на веб-страницах. React значительно облегчает создание интерфейсов благодаря разбиению каждой страницы на небольшие фрагменты. Мы называем эти фрагменты компонентами.
Вот пример разбивки страницы на компоненты:
Каждый выделенный фрагмент страницы, показанной на рисунке, считается компонентом. Но что это значит для разработчика?
Что такое компонент React?
Компонент React — это, если по-простому, участок кода, который представляет часть веб-страницы. Каждый компонент — это JavaScript-функция, которая возвращает кусок кода, представляющего фрагмент страницы.
Для формирования страницы мы вызываем эти функции в определённом порядке, собираем вместе результаты вызовов и показываем их пользователю.
Напишем компонент внутри тега
Функции можно писать и так:
React использует язык программирования, называемый JSX, который похож на HTML, но работает внутри JavaScript, что отличает его от HTML.
Вы можете добавить сюда обычный HTML для того, чтобы он попал в пользовательский интерфейс:
Можно и написать собственный компонент на JSX. Делается это так:
Это — стандартный подход — вызывать компоненты так, будто вы работаете с HTML.
Сборка компонентов
Компоненты React можно помещать в другие компоненты.
Вот что выведет вышеприведённый код:
Именно так страницы собирают из фрагментов, написанных на React — вкладывая компоненты друг в друга.
Классы компонентов
До сих пор мы писали компоненты в виде функций. Их называют функциональными компонентами. Однако, компоненты можно писать и иначе, в виде классов JavaScript. Их называют классами компонентов.
В том случае, если вас интересуют компоненты без состояния, предпочтение следует отдать функциональным компонентам, их, в частности, легче читать. О состоянии компонентов мы поговорим ниже.
JavaScript в JSX
В JSX-код можно помещать переменные JavaScript. Выглядит это так:
Теперь текст «I am a string» окажется внутри тега
Кроме того, тут можно делать и вещи посложнее, вроде вызовов функций:
Вот как будет выглядеть страница после обработки вышеприведённого фрагмента кода:
Подводные камни JSX
Для того, чтобы этого добиться, нужно воспользоваться свойством className :
Особенности создаваемого компонента
Метод constructor компонента React всегда должен вызвать super(props) прежде чем выполнять что угодно другое.
Итак, а что нам делать с этим «состоянием»? Зачем оно придумано?
Изменение компонента React на основе его состояния
Состояние — это инструмент, позволяющий обновлять пользовательский интерфейс, основываясь на событиях. Тут мы будем использовать состояние для изменения внешнего вида кнопки проигрывания музыки, основываясь на щелчке по ней. Кнопка может отображаться в одном из двух вариантов. Первый указывает на возможность запуска проигрывания, второй — на то, что музыка проигрывается, и этот процесс можно приостановить. Когда пользователь щёлкает по кнопке, меняется состояние, а затем обновляется пользовательский интерфейс.
В функции render ключевое слово this всегда ссылается на компонент, внутри которого она находится.
Как компонент реагирует на события?
Пользователь может взаимодействовать с компонентом, щёлкая по кнопке проигрывания музыки. Мы хотим реагировать на эти события. Делается это посредством функции, которая занимается обработкой событий. Эти функции так и называются — обработчики событий.
Когда пользователь щёлкает по тексту, представленному тегом
Как должен работать компонент
Теперь, разобравшись с этим механизмом, займёмся обработкой щелчка по кнопке.
Обмен данными между компонентами
Когда состояние Container меняется, свойство PlayButton также меняется, и функция PlayButton вызывается снова. Это означает, что вид компонента на экране обновится.
Внутри PlayButton мы можем реагировать на изменения, так как PlayButton принимает свойства как аргумент:
Если мы поменяем состояние на this.state = < isMusicPlaying: true >; и перезагрузим страницу, на ней должна появиться кнопка паузы:
События как свойства
Свойства необязательно должны представлять собой какие-то данные. Они могут быть и функциями.
Неприятная особенность setState
Поэтому вот так поступать не следует:
Если вы изменяете состояние, основываясь на предыдущем состоянии, нужно делать это по-другому. А именно, следует передать setState функцию, а не объект. Эта функция принимает старое состояние как аргумент и возвращает объект, представляющий новое состояние.
Эта конструкция сложнее, но она необходима только в том случае, если вы используете старое состояние для формирования нового состояния. Если нет — можно просто передавать setState объект.
Что такое ссылки?
Пришло время включить музыку. Для начала добавим тег :
React.js для новичков в программировании: что это, как устроен и зачем нужен
Если стажёр или совсем зелёный джун попросят вас объяснить, что такое React.js, — просто покажите им эту статью.
OlyaSnow для Skillbox Media
В интернете полно руководств по React.js с названиями типа for dummies, for idiots — вроде бы для чайников. Но они по большей части негуманны и довольно сложны — человеку без знания JavaScript пользы не будет, только сильнее запутается и почувствует себя тем самым dummy. Поэтому мы решили максимально просто объяснить, что такое React.js, для чего он нужен, как попробовать и что понадобится для полноценной работы.
Что это ещё за новый тикток такой?
React.js — это JavaScript-библиотека от Facebook для удобной разработки интерфейсов, то есть внешней части сайтов и приложений, с которой взаимодействует пользователь.
Главная фишка React.js — компоненты и состояния.
Компонент — это кусочек кода, который отвечает за внешний вид одного из элементов сайта или приложения. Причём такие кусочки-компоненты могут быть вложенными.
Состояние — это вся информация об элементе, в том числе о его отображении. Например, состояние объекта «термометр» может описываться свойствами current_temperature, min и max.
Фанат Free Software Foundation, использует Linux и недолюбливает Windows. Пишет истории про кодинг и программы на Python. Влюблён в Lisp, но пока что не умеет на нём программировать.
Переводим на понятный язык: что такое компоненты и состояния
Пока звучит немного абстрактно и сложно, но на деле всё гораздо проще. Потренируемся на Цукерберге: в конце концов, это его детище — ему и отвечать.
На скриншоте три крупных компонента — пост в ленте Facebook, блок краткой информации и блок с выводом фотографий. В каждый из них входят более мелкие компоненты. Например, внутри поста есть текст, изображение, аватарка, имя автора, лайки, комментарии, различные информационные элементы. Внутри блока с фотографиями — отдельные фото, а внутри блока с краткой информацией — собственно, та самая краткая информация.
У каждого из этих компонентов есть состояния. Например, блок краткой информации будет по-разному выглядеть на мобильной и десктопной версии, у сердечка меняется цифра с лайками или цвет (если вы лайкнули или не лайкнули запись), а пост может обрезать текст, показывать содержимое полностью, меняться в зависимости от содержания. Ведь содержание поста — это тоже его состояние. Именно в этом проявляется гибкость и сила React.js: вы пишете компонент один раз, а потом просто передаёте ему разные состояния.
Посмотрите, как в зависимости от состояния меняется размер аватарки:
Уже получилось три состояния, но это не предел — ведь внешний вид аватарки различается в мобильной и десктопной версии, в приложении для Android, iOS и так далее. Отметим, что аватарка практически везде будет вложенной — в составе более крупных компонентов React.js, таких как пост, шапка, боковая панель или меню.
В React.js есть собственные средства для управления состояниями, но на практике в средних и крупных проектах чаще используют Redux — сторонний менеджер состояний. Он удобнее и лучше масштабируется.
Зачем нужен React.js, если есть HTML, JavaScript и CSS
Никаких огородов. React.js — это всего лишь способ в удобном виде представить код JavaScript и HTML, сделать его повторяемым и наглядным. Компоненты React.js пишут на особом языке — JSX, который выглядит как смесь JavaScript и HTML. Вот пример кода на этом языке:
JSX-код — то, что кажется HTML-тегом
, на самом деле элемент JSX
Код JSX довольно наглядный, и, кстати, то, что похоже на HTML, — вовсе не HTML 
Браузеру понимать JSX не нужно — код React.js транслируется в JavaScript, с которым знаком любой уважающий себя браузер. Для этого написанное на React.js приложение прогоняют, например, через Babel — специальную программу-транспайлер, которая переводит разные представления (то есть языки вроде JSX) в JavaScript-код.
JSX-код JavaScript-код после Babel
На первый взгляд, механика странная. Кажется, это как если бы Достоевский придумал свой собственный язык, писал на нём книги, а потом их переводили бы на русский и только после этого издавали. И это была бы хорошая аналогия — но только если бы книги Достоевского переводились на русский автоматически, а писать на новом языке было бы в несколько раз быстрее.
У React.js есть три мощных преимущества:
React: лучшие практики
Разрабатываете на React или просто интересуетесь данной технологией? Тогда добро пожаловать в мой новый проект — Тотальный React.
Введение
Я работаю с React уже 5 лет, однако, когда дело касается структуры приложения или его внешнего вида (дизайна), сложно назвать какие-то универсальные подходы.
Вместе с тем, существуют определенные приемы написания кода, которые позволяют обеспечить возможность долгосрочной поддержки и масштабируемости создаваемых проектов.
Данная статья представляет собой своего рода набор правил разработки React-приложений, доказавших свою эффективность для меня и команд, с которыми я работал.
Эти правила касаются компонентов, структуры приложения, тестирования, стилизации, управления состоянием и получения данных. Приводимые примеры намеренно упрощены с целью акцентирования внимания на общих принципах, а не на конкретной реализации.
Предлагаемые подходы не являются истинной в последней инстанции. Это всего лишь мое мнение. Существует много разных способов решения одной и той же задачи.
Компоненты
Функциональные компоненты
Отдавайте предпочтение функциональным компонентам — они имеют более простой синтаксис. В них отсутствуют методы жизненного цикла, конструкторы и шаблонный код. Они позволяют реализовать такую же логику, что и классовые компоненты, но меньшими усилиями и более наглядным способом (код компонентов легче читать).
До тех пор, пока вам не потребуются предохранители, используйте функциональные компоненты. Ментальная модель, которую надо держать в голове, будет намного проще.
Согласованные (последовательные) компоненты
Придерживайтесь одного стиля при создании компонентов. Помещайте вспомогательные функции в одно и тоже место, используйте одинаковый экспорт (по умолчанию или по названию) и одинаковый подход к именованию компонентов.
У каждого подхода имеются свои преимущества и недостатки.
Неважно, как вы экспортируете компоненты, в самом низу или при определении, просто придерживайтесь одного правила.
Названия компонентов
Всегда именуйте компоненты. Это помогает анализировать трассировку стека ошибки при использовании инструментов разработчика React.
Это также помогает определить, разработкой какого компонента вы в данный момент занимаетесь.
Вспомогательные функции
Функции, которым не требуются данные, хранящиеся в компоненте, должны находиться за пределами (снаружи) компонента. Для этого идеально подходит место перед определением компонента, чтобы код можно было изучать сверху вниз.
Это уменьшает «шум» компонента — в нем остается только самое необходимое.
Внутри компонента должно находиться минимальное количество вспомогательных функций. Поместите их снаружи, переадвая значения из состояния в качестве аргументов.
Соблюдение правил создания «чистых» функций позволяет легче отслеживать ошибки и расширять компонент.
Статическая (жесткая) разметка
Не создавайте статическую разметку для навигации, фильтров или списков. Вместо этого, создайте объект с настройками и переберите его в цикле.
Это означает, что, при необходимости, вам нужно будет изменить разметку и элементы только в одном месте.
Размеры компонентов
Компонент — это всего лишь функция, принимающая пропы и возвращающая разметку. Они следуют тем же принципам проектирования.
Если функция выполняет слишком много задач, вынесите часть логики в другую функцию. Тоже самое справедливо в отношении компонентов — если в компоненте содержится слишком сложный функционал, разделите его на несколько компонентов.
Если часть разметки является сложной, включает циклы или условия — извлеките ее в отдельный компонент.
Полагайтесь на пропы и коллбеки для взаимодействия и получения данных. Количество строк кода далеко не всегда является объективным критерием его качества. Всегда помните об отзывчивости и абстракции.
Комментарии в JSX
При необходимости разъяснения происходящего создайте блок комментария и внесите туда необходимую информацию. Разметка — это часть логики, так что, если вы чувствуете, что какую-то часть нужно прокомментировать, сделайте это.
Предохранители
Ошибка, возникшая в компоненте, не должна приводить к «поломке» пользовательского интерфейса. Существуют редкие случаи, когда мы хотим, чтобы критическая ошибка привела к отказу в работоспособности приложения или перенаправлению. В большинстве случаев, достаточно убрать определенный элемент с экрана.
В функции, запрашивающей данные, у нас может быть любое количество блоков «try/catch». Используйте предохранители не только на верхнем уровне приложения, но оборачивайте им каждый компонент, в котором потенциально может быть выброшено исключение во избежание каскада ошибок.
Деструктуризация пропов
Большая часть компонентов — функции, принимающие пропы и возвращающие разметку. В обычной функции мы используем аргументы, передаваемые ей напрямую, так что в случае с компонентами имеет смысл придерживаться аналогичного подхода. Нет необходимости везде повторять «props».
Причина не использовать деструктуризацию может состоять в разнице между внешним и внутренним состояниями. Однако, в обычной функции разницы между аргументами и переменными не существует. Не нужно все усложнять.
Количество пропов
Ответ на вопрос о количестве пропов является очень субъективным. Количество пропов, передаваемых в компонент, коррелируется с количеством используемых компонентом переменных. Чем больше пропов передается в компонент, тем выше его ответственность (имеется ввиду количество решаемых компонентом задач).
Большое количество пропов может свидетельствовать о том, что компонент делает слишком много.
Если в компонент передается больше 5 пропов, я задумываюсь о необходимости его разделения. В некоторых случаях компоненту просто требуется много данных. Например, поле для ввода текста может нуждаться в большом количестве пропов. С другой стороны, это верный признак того, что часть логики необходимо извлечь в отдельный компонент.
Обратите внимание: чем больше пропов принимает компонент, чем чаще он перерисовывается.
Передача объекта вместо примитивов
Один из способов уменьшить количество передаваемых пропов — передавать объект вместо примитивов. Вместо того, чтобы, например, передавать имя пользователя, его адрес электронной почты и т.д. по одному, можно их сгруппировать. Это также облегчит процесс добавления новых данных
Условный рендеринг
В некоторых случаях использование коротких вычислений (оператора «логическое И» — &&) для условного рендеринга может привести к отображению 0 в UI. Во избежание этого используйте тернарный оператор. Единственным недостатком такого подхода является чуть большее количество кода.
Оператор «&&» уменьшает количество кода, что здорово. Тернарник является более «многословным», зато всегда работает корректно. Кроме того, добавление альтернативного варианта при необходимости становится менее трудоемким.
Вложенные тернарные операторы
Тернарные операторы становится трудно читать после первого уровня вложенности. Несмотря на то, что тернарники экономят пространство, лучше явным и очевидным образом выражать свои намерения.
Списки
Перебор элементов списка — частая задача, обычно решаемая с помощью метода «map()». Как бы то ни было, в компоненте, содержащем много разметки, дополнительный отступ и синтаксис «map()» не способствуют повышению читаемости.
При необходимости перебора элементов, извлеките их в отдельный компонент, даже если разметка небольшая. Родительскому компоненту не нужны детали, ему достаточно «знать», что в определенном месте рендерится список.
Итерацию можно оставить в компоненте, единственной задачей которого является отображение списка. Если разметка списка является сложной и длинной, ее лучше извлечь в отдельный компонент.
Пропы по умолчанию
Одним из способов определения пропов по умолчанию является добавления к компоненту атрибута «defaultProps». Однако, при таком подходе функция компонента и значения для ее аргументов будут находиться в разных местах.
Поэтому более предпочтительным является присвоение «дефолтных» значений при деструктуризации пропов. Это облегчает чтение кода сверху вниз и позволяет держать в одном месте определения и значения.
Вложенные функции рендеринга
При необходимости извлечения логики или разметки из компонента, не помещайте их в функцию в том же компоненте. Компонент — это функция. Значит, извлеченная часть кода будет представлена в виде вложенной функции.
Это означает, что вложенная функция будет иметь доступ к состоянию и данным внешней функции. Это делает код менее читаемым — что делает эта функция (за что она отвечает)?
Перенесите вложенную функцию в отдельный компонент, присвойте ей имя и полагайтесь на пропы вместо замыкания.
Управление состоянием
Редукторы
Порой нам требуется более мощный способ определения и управления состоянием, чем «useState()». Попробуйте использовать «useReducer()» перед обращением к сторонним библиотекам. Это отличный инструмент для управления сложным состоянием, не требующий использования зависимостей.
В комбинации с контекстом и TypeScript, «useReducer()» может быть очень мощным. К сожалению, его используют не очень часто. Люди предпочитают применять специальные библиотеки.
Если вам требуется несколько частей состояния, переместите их в редуктор:
Хуки против HOC и рендер-пропов
В некоторых случаях нам требуется «усилить» компонент или предоставить ему доступ к внешним данным. Существует три способа это сделать — компоненты высшего порядка (HOC), рендеринг через пропы и хуки.
Наиболее эффективным является использование хуков. Они в полной мере соответствуют философии, согласно которой компонент — это функция, использующая другие функции. Хуки позволяют получать доступ к нескольким источникам, содержащим внешний функционал, без угрозы возникновения конфликта между этими источниками. Количество хуков не имеет значения, мы всегда знаем, откуда получили значение.
HOC получают значения в виде пропов. Не всегда очевидно, откуда приходят значения, из родительского компонента или из его обертки. Кроме того, цепочка из нескольких пропов является хорошо известным источником ошибок.
Использование рендер-пропов приводит к глубокой вложенности и плохой читаемости. Размещение нескольких компонентов с рендер-пропами в одном дереве еще более усугубляет ситуацию. Кроме того, они только используют значения в разметке, так что логику получения значений приходится писать здесь же или получать ее извне.
В случае с хуками мы работает с простыми значениями, которые легко отслеживать и которые не смешиваются с JSX.
Библиотеки для получения данных
Очень часто данные для состояния «приходят» из API. Нам необходимо сохранять их в памяти, обновлять и получать в нескольких местах.
Современные библиотеки, такие как React Query, предоставляют достаточное количество инструментов для управления внешними данными. Мы можем кэшировать данные, удалять их и запрашивать новые. Данные инструменты могут также использоваться для отправки данных, запуске обновления другой части данных и т.д.
Работать с внешними данными еще легче, если вы используете GraphQL-клиент наподобие Apollo. Он реализует концепцию состояния клиента из коробки.
Библиотеки для управления состоянием
В подавляющем большинстве случаев нам для управления состоянием приложения не нужны никакие библиотеки. Они требуются лишь в очень больших приложениях с очень сложным состоянием. В таких ситуациях я использую одно из двух решений — Recoil или Redux.
Ментальные модели компонентов
Контейнер и представитель
Обычно, принято разделять компоненты на две группы — представители и контейнеры или «умные» и «глупые».
Суть в том, что некоторые компоненты не содержат состояния и функционала. Они просто вызываются родительским компонентом с некоторыми пропами. Компонент-контейнер, в свою очередь, содержит некую бизнес-логику, отправляет запросы на получение данных и управляет состоянием.
Данная ментальная модель по факту описывает паттерн проектирования MVC для серверных приложений. Там она прекрасно работает.
Но в современных клиентских приложениях такой подход себя не оправдывает. Помещение всей логики в несколько компонентов приводит к их чрезмерному «раздутию». Это приводит к тому, что один компонент решает слишком много задач. Код такого компонента тяжело поддерживать. При росте приложения поддержка кода в надлежащем состоянии становится практически невозможной.
Компоненты с состоянием и без
Разделяйте компоненты на компоненты с состоянием и без. Упомянутая выше ментальная модель предполагает, что небольшое количество компонентов должно управлять логикой всего приложения. Данная модель предполагает разделение логики на максимально возможное количество компонентов.
Данные должны находиться максимально близко к компоненту, в котором они используются. При использовании клиента GrapQL мы получаем данные в компоненте, который эти данные отображает. Даже если это не компонент верхнего уровня. Не думайте о контейнерах, думайте об ответственности компонента. Определяйте наиболее подходящий компонент для хранения части состояния.
Например, компонент должен содержать данные формы. Компонент должен получать значения и вызывать коллбеки. Компонент должен уведомлять форму о желании пользователя отправить данные на обработку и т.д.
Кто отвечает за валидацию формы? Поле для ввода? Это будет означать, что данный компонент отвечает за бизнес-логику приложения. Как он будет сообщать форме о возникновении ошибки? Как будет обновляться состояние ошибки? Будет ли форма «знать» о таком обновлении? Если возникла ошибка, получится ли отправить данные на обработку?
При возникновении подобных вопросов становится очевидным, что имеет место смешивание обязанностей. В данном случае «инпуту» лучше оставаться компонентом без состояния и получать сообщения об ошибках от формы.
Структура приложения
Группировка по маршруту/модулю
Группировка по контейнерам и компонентам делает приложение сложным в изучении. Определения того, к какой части приложения относится конкретный компонент, предполагает «близкое» знакомство со всей кодовой базой.
Не все компоненты одинаковые — некоторые испольузются глобально, другие созданы для решения специфических задач. Такая структура подходит для небольших проектов. Однако, для средних и больших проектов такая структура неприемлима.
С самого начала группируйте компоненты по маршруту/модулю. Такая структура обеспечивает возможность длительной поддержки и расширения. Это не позволит приложению перерасти его архитектуру. Если полагаться на «контейнерно-компонентную архитектуру», то это произойдет очень быстро.
Основанная на модулях архитектура легко масштабируется. Вы просто добавляете новые модули, при этом не увеличивая сложность системы.
«Контейнерная архитектура» не является неправильной, но она не очень общая (абстрактная). Она не скажет тому, кто ее изучает ничего, кроме того, что для разработки приложения используется React.
Общие модули
Такие компоненты, как кнопки, поля для ввода и карточки используются повсеместно. Даже если вы не используете структуру, основанную на компонентах, извлекайте их в общие компоненты.
Так вы будете видеть, какие общие компоненты используются в вашем приложении, даже без помощи Storybook. Это позволяет избежать дублирования кода. Вы же не хотите, чтобы каждый член вашей команды разрабатывал собственный вариант кнопки? К сожалению, это часто происходит из-за плохой архитектуры приложения.
Абсолютные пути
Отдельные части приложения должны меняться настолько легко, насколько это возможно. Это относится не только к коду компонента, но и к месту его расположения. Абсолютные пути означают, что вам не придется ничего менять при перемещении импортируемого компонента в другое место. Кроме того, это облегчает определение расположения компонента.
Я использую префикс «@» в качестве индикатора внутреннего модуля, но я также видел примеры использования символа «
Оборачивание внешних компонентов
Старайтесь не импортировать слишком много сторонних компонентов напрямую. Создавая адаптер для таких компонентов мы можем при необходимости модифицировать их API. Также мы можем менять используемые библиотеки в одном месте.
Это относится как к библиотекам компонентов, таким как Semantic UI, так и к утилитам. Простейший способ заключается в повторном экспорте таких компонентов из общего модуля.
Компоненту не нужно знать, какую конкретно библиотеку мы используем.
Один компонент — одна директория
Я создаю директорию для компонентов для каждого модуля в моих приложениях. Сначала я создаю компонент. Затем, если возникает необходимость в дополнительных файлах, относящихся к компоненту, таких как стили или тесты, я создаю директорию для компонента и размещаю все файлы в ней.
Хорошей практикой является создание файла «index.js» для повторного экспорта компонента. Это позволяет не изменять пути импорта и избежать дублирования названия компонента — «import Form from ‘components/UserForm/UserForm’». Однако, не следует помещать код компонента в файл «index.js», поскольку это сделает невозможным поиск компонента по названию вкладки в редакторе кода.
Производительность
Преждевременная оптимизация
Перед началом оптимизации убедитесь в том, что для этого существуют причины. Слепое следование лучшим практикам — пустая трата времени, если это не оказывает никакого влияния на приложение.
Разумеется, думать о таких вещах, как оптимизация, нужно, но предпочтение следует отдавать разработке читаемых и поддерживаемых компонентов. Хорошо написанный код легче улучшать.
Если вы заметили проблемы с производительностью приложения, измерьте ее и определите причину. Нет смысла уменьшать количество повторных рендерингов при огромном размере «бандла».
После определения проблем, устраните их в порядке влияния на производительность.
Размер сборки
Количество JavaScript, отправляемое браузеру, это ключевой фактор производительности приложения. Само приложение может быть очень быстрым, но об этом никто не узнает, если для его запуска придется предварительно загружать 4 Мб JavaScript.
Не стремитесь к одному «бандлу». Разделяйте приложение на уровне маршрутов и даже больше. Убедитесь, что отправляете браузеру минимальное количество кода.
Осуществляйте подгрузку в фоновом режиме или при выражении пользователем намерения получить другую часть приложения. Если нажатие кнопки запускает загрузку PDF-файла, вы можете начать загрузку соответствующей библиотеки в момент наведения курсора на кнопку.
Повторный рендеринг — коллбеки, массивы и объекты
Необходимо стремиться к снижению количества повторных рендерингов компонентов. Помните об этом, на также имейте ввиду, что ненужные повторные рендеринги редко оказывают существенное влияние на приложение.
Не передавайте коллбеки в виде пропов. При таком подходе функция каждый раз создается заново, запуская повторный рендеринг.
Если вы столкнулись с проблемами производительности, причиной которых являются замыкания, избавьтесь от них. Но не делайте код менее читаемым или слишком «многословным».
Явная передача массивов или объектов относится к той же категории проблем. Они сравниваются по ссылкам, так что не проходят поверхностную проверку и запускают повторный рендеринг. Если вам необходимо передать статичный массив, создайте его в качестве константы перед определением компонента. Это позволит каждый раз передавать один и тот же экземпляр.
Тестирование
Тестирование при помощи снимков
Однажды, я столкнулся с интересной проблемой при проведении snapshot-тестирования: сравнение «new Date()» без аргумента с текущей датой всегда возвращало «false».
Кроме того, снимки приводят к неудачным сборкам только при изменении компонента. Обычный рабочий процесс выглядит следующим образом: вносим изменения в компонент, проваливаем тест, обновляем снимок и продолжаем.
Важно понимать, что снимки не заменяют тестов на уровне компонента. Лично я этим видом тестирования больше не пользуюсь.
Тестирование корректного рендеринга
Основная задача тестирования заключается в подтверждении того, что компонент работает, как ожидается. Убедитесь в том, что компонент возвращает правильную разметку как с «дефолтными», так и с переданными пропами.
Также убедитесь, что функцию всегда возвращает правильные результаты для конкретных входных данных. Проверьте, что все, что вам нужно, корректно отображается на экране.
Тестирование состояния и событий
Компонент с состоянием, обычно, меняется в ответ на событие. Создайте имитацию события и проверьте, что компонент правильно на него реагирует.
Убедитесь, что вызываются обработчики и передаются верные аргументы. Проверьте правильную установку внутреннего состояния.
Тестирование пограничных случаев
После покрытия кода базовыми тестами, добавьте несколько тестов для проверки особых случаев.
Это может означать передачу пустого массива для того, чтобы убедиться в том, что доступ к индексу не осуществляется без проверки. Это также может означать вызов ошибки в компоненте (например, в запросе к API) для проверки ее правильной обработки.
Интеграционное тестирование
Интеграционное тестирование означает проверку целой страницы или большого компонента. Данный вид тестирования означает проверку работоспособности некой абстракции. Он позволяет получить более убедительный результат того, что приложение работает, как ожидается.
Отдельные компоненты могут успешно проходить юнит-тесты, но при этом взаимодействие между ними может вызывать проблемы.
Стилизация
CSS-в-JS
Это очень спорный вопрос. Лично я предпочитаю использовать библиотеки вроде Styled Components или Emotion, позволяющие писать стили в JavaScript. Одним файлом меньше. Не надо думать о таких вещах, как, например, названия классов.
Структурной единицей React является компонент, так что техника CSS-в-JS или, точнее, все-в-JS, на мой взгляд, является наиболее предпочтительной.
Обратите внимание: другие подходы к стилизации (SCSS, CSS-модули, библиотеки со стилями типа Tailwind) не являются неправильными, но я все же рекомендую использовать CSS-в-JS.
Стилизованные компоненты
Обычно, я стараюсь держать стилизованные компоненты и компонент, который их использует, в одном файле.
Тем не менее, когда стилизованных компонентов очень много, имеет смысл вынести их в отдельный файл. Я видел использование такого подхода в некоторых открытых проектах вроде Spectrum.
Получение данных
Библиотеки для работы с данными
React не предоставляет каких-либо специальных инструментов для получения или обновления данных. Каждая команда создает собственную реализацию, как правило, включающую в себя сервис для асинхронных функций, взаимодействующих с API.
Использование такого подхода означает, что отслеживание статуса загрузки и обработка HTTP-ошибок возлагается на нас. Это приводит к «многословности» и большому количеству шаблоного кода.
Вместо этого, лучше использовать такие библиотеки как React Query и SWR. Они делают взаимодействие с сервером органической частью жизненного цикла компонента идиоматическим способом — с помощью хуков.
Они имеют встроенную поддержку кэширования, управления состоянием загрузки и обработки ошибок. Также они избавляют от необходимости использования библиотек для управления состоянием для обработки этих данных.
Благодарю за внимание и хорошего начала рабочей недели.





