[Перевод] Введение в React, которого нам не хватало

React — это самая популярная в мире JavaScript-библиотека. Но эта библиотека не потому хороша, что популярна, а потому популярна, что хороша. Большинство существующих вводных руководств по React начинается с примеров того, как пользоваться этой библиотекой. Но эти руководства ничего не говорят о том, почему стоит выбрать именно React.

У такого подхода есть свои сильные стороны. Если кто-то стремится к тому, чтобы, осваивая React, тут же приступить к практике, ему достаточно заглянуть в официальную документацию и взяться за дело.

xow3dwq3dlikxmbenkn1lhwfduu.png

Этот материал (вот, если интересно, его видеоверсия) написан для тех, кто хочет найти ответ на следующие вопросы: «Почему React? Почему React работает именно так? С какой целью API React устроены так, как устроены?».

Почему React?


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

Когда появилась библиотека React — это на фундаментальном уровне изменило то, как работают JavaScript-фреймворки и библиотеки. В то время как другие подобные проекты продвигали идеи MVC, MVVM и прочие подобные, в React был выбран другой подход. А именно, тут рендеринг визуальной составляющей приложения был изолирован от представления модели. Благодаря React во фронтенд-экосистеме JavaScript появилась совершенно новая архитектура — Flux.

Почему команда разработчиков React поступила именно так? Почему такой подход лучше тех, что появились раньше него, вроде архитектуры MVC и спагетти-кода, который пишут на jQuery? Если вы из тех, кого интересуют эти вопросы, можете посмотреть это выступление 2013 года, посвящённое разработке JavaScript-приложений в Facebook.

В 2013 году компания Facebook только что завершила серьёзную работу по интеграции в свою платформу чата. Эта новая возможность была встроена практически в каждую страницу проекта, чат влиял на обычные сценарии работы с платформой. Это было сложное приложение, встроенное в другое приложение, которое и до этого нельзя было назвать простым. Команде Facebook пришлось столкнуться с решением нетривиальных задач, справляясь с неконтролируемой мутацией DOM и с необходимостью обеспечить параллельную асинхронную работу пользователей в новой среде.

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

Используя популярные фронтенд-инструменты, существовавшие до React, ничего такого гарантированно обеспечить было нельзя. В ранних веб-приложениях «состояние гонок» в DOM было одной из самых распространённых проблем.

Отсутствие детерминизма = параллельные вычисления + мутабельное состояние.

Мартин Одерски

Главной задачей команды разработки React было решение этой проблемы. Они с ней справились, применив два основных инновационных подхода:

  • Однонаправленная привязка данных с использованием архитектуры Flux.
  • Иммутабельность состояния компонента. После того, как состояние компонента установлено, оно уже не может быть изменено. Изменения состояния не затрагивают визуализированные компоненты. Вместо этого подобные изменения приводят к выводу нового представления, обладающего новым состоянием.


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

Том Оччино, JSConfUS 2013

Библиотека React смогла серьёзно снизить остроту проблемы неконтролируемых мутаций благодаря использованию архитектуры Flux. Вместо того чтобы присоединять к произвольному количеству произвольных объектов (моделей) обработчики событий, вызывающие обновления DOM, библиотека React дала разработчикам единственный способ управления состоянием компонента. Это — диспетчеризация действий, влияющих на хранилище данных. Когда меняется состояние хранилища, система предлагает компоненту перерендериться.

4ca5eec37901b758b5bc3fc89456ff4e.png


Архитектура Flux

Когда мне задают вопрос о том, почему стоит обратить внимание на React, я даю простой ответ: «Дело в том, что нам нужен детерминированный рендеринг представлений, а React значительно упрощает решение этой задачи».

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

Если бы детерминированный рендеринг компонентов был бы единственной фишкой React, то одно это уже было бы замечательной инновацией. Но команда разработчиков React на этом не остановилась. Эта команда представила миру библиотеку, обладающую и другими интереснейшими, уникальными возможностями. А по мере развития проекта в React появилось ещё больше всего полезного.

JSX


JSX — это расширение JavaScript, позволяющее декларативно создавать компоненты пользовательского интерфейса. JSX обладает следующими заметными возможностями:

  • Применение простой декларативной разметки.
  • Код разметки расположен там же, где и код компонента.
  • Реализация принципа разделения ответственностей (например — отделение описания интерфейса от логики состояния и от побочных эффектов). Причём, реализация, основанная не на использовании различных технологий (например — HTML, CSS, JavaScript).
  • Абстрагирование управления изменениями DOM.
  • Абстрагирование от особенностей различных платформ, для которых создают React-приложения. Дело в том, что благодаря использованию React можно создавать приложения, предназначенные для множества платформ (речь идёт, например, о разработке для мобильных устройств с использованием React Native, о приложениях для систем виртуальной реальности, о разработке для Netflix Gibbon, о создании Canvas/WebGL-интерфейсов, о проекте react-html-email).


Если, до появления JSX, нужно было декларативно описывать интерфейсы, то нельзя было обойтись без использования HTML-шаблонов. В те времена не было общепризнанного стандарта по созданию таких шаблонов. Каждый фреймворк использовал собственный синтаксис. Этот синтаксис приходилось изучать тому, кому, например, нужно было пройтись в цикле по неким данным, встроить в текстовый шаблон значения из переменных или принять решение о том, какой компонент интерфейса выводить, а какой — нет.

В наши дни, если взглянуть на разные фронтенд-инструменты, окажется, что без специального синтаксиса, вроде директивы *ngFor из Angular, тоже не обойтись. Но, так как JSX можно назвать надмножеством JavaScript, создавая JSX-разметку можно пользоваться существующими возможностями JS.

Например, перебрать некий набор элементов можно, воспользовавшись методом Array.prototype.map. Можно использовать логические операторы, организовывать условный рендеринг с помощью тернарного оператора. Можно пользоваться чистыми функциями, можно конструировать строки с использованием шаблонных литералов. В общем-то, тому, кто описывает интерфейсы средствами JSX, доступны все возможности JavaScript. Полагаю, что в этом заключается огромное преимущество React перед другими фреймворками и библиотеками.

Вот пример JSX-кода:

const ItemList = ({ items }) => (
  
        {items.map((item) => (       
  •         
    {item.name}
          
  •     ))}   
);


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

  • Тут используется подход к именованию атрибутов элементов, отличающийся от того, который принят в HTML. Например, class превращается в className. Речь идёт о применении стиля именования camelCase.
  • У каждого элемента списка, который нужно вывести, должен быть постоянный уникальный идентификатор, предназначенный для использования в JSX-атрибуте key. Значение идентификатора должно оставаться неизменным в ходе различных манипуляций с элементами списка. На практике большинство элементов списков в моделях данных имеют уникальные id, эти идентификаторы обычно отлично показывают себя в роли значений для key.


React не навязывает разработчику единственно правильный способ работы с CSS. Например, компоненту можно передать JavaScript-объект со стилями, записав его в свойство style. При таком подходе большинство привычных имён стилей будет заменено на их эквиваленты, записанные по правилам camelCase. Но этим возможности по работе со стилями не ограничиваются. На практике я одновременно пользуюсь разными подходами к стилизации React-приложений. Выбор конкретного подхода зависит от того, что именно нужно стилизовать. Например, глобальные стили я применяю для оформления тем приложений и макетов страниц, а локальные стили — для настройки внешнего вида конкретного компонента.

Вот мои любимые возможности React, касающиеся работы со стилями:

  • CSS-файлы, которые можно загружать в заголовочной части страницы. Они могут использоваться для настройки макетов страниц, шрифтов и прочих подобных элементов. Это — надёжный, работоспособный механизм стилизации.
  • CSS-модули — это CSS-файлы область применения которых ограничена локальной областью видимости. Их можно импортировать непосредственно в JavaScript-файлы. Для того чтобы применять CSS-модули, нужно воспользоваться правильно настроенным загрузчиком модулей. В Next.js, например, этот механизм активирован по умолчанию.
  • Пакет styled-jsx, который позволяет объявлять стили прямо в коде React-компонентов. Это напоминает использование тега