React Elements против React Components

Перевод статьи «React Elements vs React Components» от Tyler McGinnis.

Несколько месяцев назад я думал, что запостил довольно простой вопрос в Twitter.

Я тщательно подхожу к изучению языка, но у меня до сих пор нет хорошего определения для компонента (React компонента). Есть идеи?

React Elements против React Components

 

Меня удивила не общая путаница в этом вопросе, а количество неточных ответов, которые я получил:

Instances / Instantiation
Rendering
Evaluation
Invocation
“Using it :)”

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

Давайте начнём с рассмотрения основ React. Что такое React? Это библиотека для разработки пользовательских интерфейсов. Как бы сложно React и его экосистема не выглядела, это React и его суть — разработка UI. С этой мыслью, мы подходим к нашему первому определению, Элемент (Element). Простыми словами, React элемент описывает то что вы хотите увидеть экране. А если не простыми, то React элемент описывает узел DOM в виде объекта. Обратите внимание на то, что я использовал слово описывает. Важно, что React элемент это не то что вы увидите на экране, а он описывает то что вы увидите. Для этого есть несколько причин. Первая причина заключается в том, что JavaScript объекты достаточно лёгкие и React может создавать и уничтожать их без слишком большого оверхэда. Вторая причина заключается в том, что React может анализировать объект и анализировать настоящий DOM, а затем обновлять DOM только в том месте, где произошли изменения. Это даёт некоторые плюсы в плане производительности.

Для того чтобы создать объект описывает DOM узел (он же React элемент), мы можем воспользоваться методом createElement.

createElement принимает три аргумента. Первый это название тега (div, span и т.д.), второй это атрибуты которые вы хотите задать элементу и третий это содержимое или дочерний элемент, в нашем случае это текст «Login». createElement вызываемый выше вернёт следующий объект:

и когда он будет отображён в DOM (с помощью функции ReactDOM.render), у нас появиться новый DOM узел, который будет выглядеть следующим образом:

Пока что всё хорошо. Что действительно интересно в изучении React, это то что как правило учат в первую очередь — компоненты. Компоненты — это строительные блоки в React. Однако, заметьте что мы начали эту статью с элементов. Причина по которой мы это сделали, заключается в том, что если вы поймёте элементы, то плавно перейдёте к пониманию компонентов. React компонент — это функция или класс, который принимает входные данные (опционально) и возвращает React элемент.

По  определению, у нас есть компонент Button, который принимает функцию onLogin и возвращает React элемент. Надо отметить, что компонент Button получает функцию onLogin в качестве своего свойства. Чтобы передать её в наш объект описывающий DOM, мы передаём её в качестве второго аргумента метода createElement, также как мы передавали наш атрибут id.

Давайте углубимся.

До этого момента, мы создавали элементы только с помощью нативных HTML элементов (div, span и т.д.), но мы также можем передавать другие React компоненты в качестве первого аргумента функции createElement.

Однако, в отличии от имени HTML тега, если React увидит класс или функцию в качестве первого аргумента, он проверит какой элемент будет отображаться, учитывая соответствующие свойства. React будет рекурсивно продолжать делать это, пока не останется вызовов createElement без функций или классов в качестве первого аргумента. Давайте посмотрим на это в действии.

В примере выше, у нас есть два компонента, Button и User. Объект User будет отображён как элемент «div» с двумя дочерними элементами, «p» который оборачивает имя пользователя и компонент Button. Теперь давайте заменим вызовы функции createElement на то что они возвращают:

Вы заметили, что вы приведённом выше примере у нас есть 4 разных свойства «type» — «button», «div», «p», and Button. Когда React увидит элемент с функцией или классом в типе (такой как «type: Button» выше), он обратиться к этому компоненту, чтобы узнать что он возвращает, учитывая соответствующие свойства. В конце этого процесса у React есть полный объект представления дерева DOM. В нашем примере оно выглядит следующим образом:

Весь этот процесс в React называется «сверка» (reconciliation) и срабатывает каждый раз, когда вызываются функции setState или ReactDOM.render.

Теперь давайте опять взглянем на наш первоначальный вопрос:

На данный момент у нас есть все необходимые знания для ответа на этот вопрос, за исключением одной важной части. Скорей всего, если вы используете React уже на протяжении какого-то времени, то вы не используете функцию React.createElement для создания объектов описывающих DOM. Вместо этого, вероятно вы используете JSX. Ранее я писал, что «Такая путаница происходит из-за того, что мы часто не говорим об абстрактном слое между JSX и тем, что на самом деле происходит в React». Этот абстрактный слой заключается в том, что JSX всегда компилируется в вызов React.createElement с помощью Babel.

Взгляните на код, из нашего предыдущего примера:

Это результат компиляции следующего JSX кода:

И наконец, как называется действие, когда напишем наш компонент как этот, <Icon />?

Мы можем называть это «созданием элемента», потому что после того как JSX будет скомпилирован, именно это и произойдёт.

Все примеры, «создания React элемента»:

Для получения более полной информации прочитайте статью «React Components, Instances, and Elements» от Dan Abramov.