Несколько месяцев назад я разместил в Твиттере, как мне показалось, простой вопрос:
Что меня удивило, так это не общая путаница вокруг этого вопроса, а количество полученных мной неточных ответов.
Экземпляры / Создание экземпляров
Отрисовка
Оценка
Вызов
«Использование :)»
Основная причина путаницы заключается в том, что часто не обсуждается уровень абстракции между JSX и тем, что на самом деле происходит в мире React. Чтобы ответить на этот вопрос, нам нужно глубоко погрузиться в эту абстракцию.
Давайте начнем с изучения основ React.
Что такое React?
React - это библиотека для создания пользовательских интерфейсов. Независимо от того, насколько сложной кажется React или экосистема React, это React по своей сути - создание пользовательского интерфейса. Имея это в виду, мы приходим к нашему первому определению - элемент.
Проще говоря, элемент React описывает то, что вы хотите видеть на экране.
Не так просто, элемент React - это объектное представление узла DOM.
Обратите внимание, что я использовал слово описать. Важно отметить, что элемент React - это не то, что вы видите на экране, а просто его объектное представление. На это есть несколько причин:
- Объекты JavaScript легковесны. React может создавать и уничтожать эти элементы без особых накладных расходов.
- React может анализировать объект, затем анализировать фактическую DOM, а затем обновлять фактическую DOM только там, где произошло изменение. Это имеет некоторые преимущества в производительности.
Чтобы создать объектное представление узла DOM (также известного как элемент React), мы можем использовать метод React createElement.
const element = React.createElement(
'div',
{id: 'login-btn'},
'Login'
)
createElement
принимает три аргумента:
- строка имени тега (div, span и т. д.)
- любые атрибуты, которые вы хотите, чтобы элемент имел
- содержимое или дочерние элементы элемента - в данном случае текст «Логин».
Вызов createElement
, приведенный выше, вернет объект с этой формой:
{
type: 'div',
props: {
children: 'Login',
id: 'login-btn'
}
}
И когда он будет отображен в DOM (с использованием ReactDOM.render), у нас будет новый узел DOM, который выглядит следующим образом:
<div id='login-btn'>Login</div>
Все идет нормально. Что интересно в изучении React, так это то, что обычно первое, чему вас учат, - это компоненты. «Компоненты - это строительные блоки React».
Обратите внимание, однако, что мы начали этот пост с элементов. Причина этого в том, что если вы понимаете элементы, понимание компонентов становится плавным переходом.
Компонент - это функция или класс, который дополнительно принимает ввод и возвращает элемент React.
function Button ({ onLogin }) {
return React.createElement(
'div',
{id: 'login-btn', onClick: onLogin},
'Login'
)
}
По определению у нас есть компонент Button, который принимает вход onLogin и возвращает элемент React. Следует отметить, что компонент Button получает в качестве свойства метод onLogin. Чтобы передать это нашему объектному представлению DOM, мы передаем его в качестве второго аргумента createElement, так же, как мы делали наш атрибут id.
Пойдем глубже.
До этого момента мы рассматривали только создание элементов React со свойством «type» собственных элементов HTML («span», «div» и т. Д.), Но вы также можете передать другие компоненты React в первый аргумент createElement.
const element = React.createElement(
User,
{name: 'Tyler McGinnis'},
null
)
Однако, в отличие от имени тега HTML, если React видит класс или функцию в качестве первого аргумента, он затем проверит, какой элемент он отображает, учитывая соответствующие свойства. React будет продолжать делать это до тех пор, пока больше не будет вызовов createElement, которые имеют класс или функцию в качестве первого аргумента. Давайте посмотрим на это в действии.
function Button ({ addFriend }) { return React.createElement( "button", { onClick: addFriend }, "Add Friend" ) }
function User({ name, addFriend }) { return React.createElement( "div", null, React.createElement( "p", null, name ), React.createElement(Button, { addFriend }) ) }
Выше у нас есть два компонента. Кнопка и пользователь. Пользовательским объектным представлением DOM будет «div» с двумя дочерними элементами: «p», заключающий в себе имя пользователя, и компонент Button. Теперь давайте заменим вызовы createElement тем, что они возвращают,
function Button ({ addFriend }) { return { type: 'button', props: { onClick: addFriend, children: 'Add Friend' } } }
function User ({ name, addFriend }) { return { type: 'div', props: { children: [{ type: 'p', props: { children: name } }, { type: Button, props: { addFriend } }] } } }
Вы заметите, что в приведенном выше коде у нас есть четыре различных свойства типа: «button», «div», «p» и «Button». Когда React видит элемент с функцией или типом класса (например, наш “type: Button”
выше), он затем консультируется с этим компонентом, чтобы узнать, какой элемент он возвращает, учитывая соответствующие свойства.
Имея это в виду, в конце этого процесса React имеет полное объектное представление дерева DOM. В нашем примере это будет выглядеть так:
{
type: 'div',
props: {
children: [{
type: 'p',
props: { children: 'Tyler McGinnis' }
},
{
type: 'button',
props: {
onClick: addFriend,
children: 'Add Friend'
}
}]
}
}
Весь этот процесс в React называется согласованием и запускается каждый раз при вызове setState
или ReactDOM.render
.
Итак, теперь давайте снова взглянем на наш первоначальный вопрос, который вызвал появление этого сообщения в блоге:
На данный момент у нас есть все знания, необходимые для ответа на этот вопрос, за исключением одной важной части.
Скорее всего, если вы использовали React какое-то время, вы не используете React.createElement
для создания объектных представлений DOM. Вместо этого вы, вероятно, используете JSX.
Ранее я писал: «Основная причина путаницы в том, что часто не обсуждается уровень абстракции между JSX и тем, что на самом деле происходит в мире React». Этот уровень абстракции заключается в том, что JSX всегда будет транслироваться в React.createElement
вызовы, обычно через Babel .
Глядя на наш предыдущий пример, этот код:
function Button ({ addFriend }) { return React.createElement( "button", { onClick: addFriend }, "Add Friend" ) }
function User({ name, addFriend }) { return React.createElement( "div", null, React.createElement( "p", null, name), React.createElement(Button, { addFriend }) ) }
является результатом транспиляции этого JSX.
function Button ({ addFriend }) { return ( <button onClick={addFriend}>Add Friend</button> ) }
function User ({ name, addFriend }) { return ( <div> <p>{name}</p> <Button addFriend={addFriend}/> </div> ) }
Итак, наконец, как мы это называем, когда пишем наш компонент вот так, <Icon/>
?
Мы можем назвать это «созданием элемента», потому что именно это и происходит после того, как JSX передан.
React.createElement(Icon, null)
Все эти примеры «создают элемент React».
React.createElement( 'div', className: 'container', 'Hello!' )
<div className='container'>Hello!</div> <Hello />
Спасибо за прочтение! Для получения дополнительной информации по этой теме прочтите Компоненты, экземпляры и элементы React Дэна Абрамова.
Следуйте за Тайлером МакГиннисом в Twitter ⚛️
Первоначально опубликовано на tylermcginnis.com.