Подробное руководство по JSX в React
Почему-то довольно мало внимания уделяется именно глубокому понимаю JSX, хотя он необходим для полноценной и быстрой работы с React. Я не нашел в рунете действительно подробного руководства по JSX в React и перевел эту статью, добавив немного от себя.
Введение в JSX
JSX это технология, которая была представлена React.
Хоть React может работать без использования JSX, это всё же идеальный помощник для работы с компонентами, так что React во многом выигрывает, применяя JSX.
Сначала вы можете подумать, что использование JSX это смесь HTML и JavaScript (И как вы далее заметите еще и CSS).
Но это не так, так как то, что вы реально делаете при использовании JSX синтаксиса, это написание того же самого декларативного синтаксиса, но только тем, чем должен быть компонент UI.
И вы обрисовываете UI не используя строки, а используя JavaScript, с которым можно делать много всего интересного и полезного.
Этюд к JSX
Вот как вы обозначите h1
тег, включающий в себя строку:
const element = <h1>Hello, world!</h1>
Это смахивает на странный микс JavaScript и HTML, но в реальности это всё чистый JavaScript.
А то, что похоже на HTML, на самом деле является синтаксическим сахаром для определения компонентов и их позиционирования внутри разметки.
Внутри JSX выражения можно совершенно легко вставить любые атрибуты:
const myId = 'test'
const element = <h1 id={myId}>Hello, world!</h1>
Вам достаточно только обратить внимание тогда, когда у атрибута есть тире, что конвертирует его в горбатый регистр и обратите внимание ещё на эти два специальных случая:
class
становится className
for
становится htmlFor
Так как это зарезервированные слова в JavaScript.
Вот пример JSX, который заключает два компонента в div
тег:
<div>
<BlogPostsList />
<Sidebar />
</div>
Тег всегда должен быть закрыт, так как это скорее XML, чем HTML (если вы помните деньки с XHTML, было схоже, но затем простой и понятный синтаксис HTML5 все таки взял верх). В этом случае используется самозакрывающийся тег.
Обратите внимание на то, как я обернул два компонента в div
. Почему? Потому, что функция render()
может отдать только один узел, так что в случае, когда вам надо вернуть двух потомков, просто добавьте родителя. Это может быть вообще любой тег, не только div
.
Транспиляция JSX (трансформирование в читаемый браузером формат)
Браузер не может понимать JS файлы, которые содержат JSX код. Сначала они должны быть трансформированы в обычный и понятный JavaScript.
Как это сделать? В процессе транспиляции.
Мы уже говорили, что JSX опционален и в принципе, необязателен в React, потому что для каждой JSX строки, есть соответствующая JavaScript альтернатива и в конечном итоге именно в нее трансформируется наш JSX.
Для примера, следующие две конструкции полностью равнозначны:
ReactDOM.render(
React.createElement('div', { id: 'test' },
React.createElement('h1', null, 'A title'),
React.createElement('p', null, 'A paragraph')
),
document.getElementById('myapp')
)
А это уже JSX:
ReactDOM.render(
<div id="test">
<h1>A title</h1>
<p>A paragraph</p>
</div>,
document.getElementById('myapp')
)
Это очень простой пример для начала понимания, но вы уже можете представить, то как более сложный синтаксис на чистом JS может быть сравним с использованием JSX.
Во время написания, самым популярным способом была транспиляция с использованием Babel, что является дефолтной опцией при запуске create-react-app
, так что, если вы её используете, то вам не стоит беспокоиться, там все произойдет само собой.
А если вы не используете create-react-app
, то вам придется настраивать Babel самому.
JS в JSX
JSX допускает использование любого, вставленного в него JavaScript.
Всякий раз когда вам нужно добавить какой-либо JS, просто вставьте его в фигурные скобки {}
.
Для примера, вот как использовать переменную, которую будет доступна отовсюду:
const paragraph = 'A paragraph'
ReactDOM.render(
<div id="test">
<h1>A title</h1>
<p>{paragraph}</p>
</div>,
document.getElementById('myapp')
)
Это простой пример. Фигурные скобки принимают любой JS код:
const paragraph = 'A paragraph'
ReactDOM.render(
<table>
{rows.map((row, i) => {
return <tr>{row.text}</tr>
})}
</table>,
document.getElementById('myapp')
)
Как вы видите, мы вложили JavaScript в JSX, объявленный внутри JavaScript, который вложен в JSX. В общем то тут вы можете пойти настолько глубоко, насколько захотите.
HTML в JSX
JSX напоминает HTML, но на самом деле это XML синтаксис.
В конечном итоге получается, что вы рендерите HTML, так что вам всего-лишь нужно знать несколько различий между тем, как вы определите некоторые вещи в HTML и как вы это сделаете в JSX.
Вам нужно закрывать все теги
JSX это как XHTML, если вы когда-либо его использовали, то вы уже знаете, что в нём надо закрывать все теги: забудьте про <br>
, а вместо этого используйте самозкрывающийся тег <br />
. Ну и также делайте с другими тегами.
Горбатый регистр это новый стандарт
В HTML вы найдете атрибуты без каких-либо изменений. В JSX они переименованы в свою вариацию на горбатом регистре:
onchange
=> onChange
onclick
=> onClick
onsubmit
=> onSubmit
class стал className
Так как JSX это JavaScript и class
это зарезервированное слово, вы не можете написать:
<p class="description">
Но вам нужно использовать:
<p className="description">
Тоже самое применяется к for
, который превращается в htmlFor
.
Атрибуты стилей изменяют свою семантику
Атрибут style
в HTML позволяет указывать инлайновые стили. В JSX это больше не принимается как строка, а в разделе CSS в React вы узнаете почему это так удобно.
CSS в React
JSX даёт клевый способ указывать CSS стили.
Если у вас есть небольшой опыт работы с инлайновым HTML, то на первый взгляд вам покажется, что вас отправили лет на 10–15 назад, в мир, где инлайновый CSS был абсолютной нормой.
Но стилизация с JSX это не тоже самое: во первых, вместо принятия строки, содержащей свойства CSS, JSX атрибут style
принимает только объект. Это означает, то что вы определяете свойства в объекте:
var divStyle = {
color: 'white'
}
ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode)
Или
ReactDOM.render(<div style={{ color: 'white' }}>Hello World!</div>, mountNode)
CSS значения, которые вы пишите в JSX, слегка другие, чем чистый CSS:
Основные названия свойств пишутся в горбатом регистре
Значения это просто строки
Вы разделяете каждое “определение” с помощью запятой
Почему это предпочтительнее над чистым CSS/SASS/LESS?
CSS это нерешенная проблема. После его появления, появились десятки инструментов, связанных с ним, которые стремительно взлетали и также быстро были забыты. Главная проблема с JS в том, что тут нет областей видимости и довольно легко написать CSS, который не работает так, как надо, следовательно, “грязные быстрые решения” могут повлиять на те элементы, которые вообще не должны быть затронуты.
JSX позволяет компонентам полностью инкапсулировать их стили.
Решение ли это?
Инлайновые стили в JSX конечно хороши, пока вам не надо: написать медиа запросы, стили анимаций, сослаться на псевдо-классы, сослаться на псевдо-элементы.
Вкратце, они покрывают основы, но это далеко не конечное решение.
Формы в JSX
JSX добавляет некоторые изменения в то, как работают формы в HTML, с целью того, чтобы упростить жизнь разработчику.
value и defaultvalue
Атрибут value
, всегда содержит дефолтное значение, которое было указано при создании поля.
Это помогает решить некоторые неординарные моменты в поведении регулярного взаимодействия с DOM, когда проверка input.value
и input.getAttribute('value')
возвращает одно настоящее значение и одно изначальное дефолтное значение.
Это также применимо к полю textarea
.
<textarea>Some text</textarea>
Но вместо:
<textarea defaultValue={'Some text'} />
Для полей select
, вместо этого:
<select>
<option value="x" selected>
...
</option>
</select>
Более последовательный onChange
Передавая функции атрибуту onChange
, вы можете подписаться на события в полях формы.
Это работает последовательно через поля, даже radio
, select
и checkbox
срабатывают по onChange
.
onChange
также срабатывает при написании символов в input
или textarea
.
JSX автоматическое экранирование
Чтобы смягчить любой возможный риск XSS искплойтов, JSX принудительно автоматически экранирует выражения.
Это означает то, что вы можете столкнуться с некоторыми затруднениями во время использования HTML в строчном выражении.
Для примера, вы ожидаете, что выдаст © 2017
:
<p>{'© 2019'}</p>
Но увы, вам выдаст © 2017
, так как строка экранизирована.
Чтобы исправить эту проблему, вы можете какбы вынести все за пределы выражения:
<p>© 2017</p>
Так и использовать константу, которая выведет Unicode представление, соответствующее HTML который вам нужно вывести:
<p>{‘\u00A9 2017’}</p>
Пробел в JSX
Чтобы добавить пробел в JSX есть два правила:
Горизонтальный пробел сокращается до одного.
Если у вас есть пробел между элементами на одной линии, то всё это сократится до одного пробела.
<p>Something becomes this</p>
Это станет:
<p>Something becomes this</p>
Вертикальные пробелы стираются:
<p>
Something
becomes
this
</p>
Это станет:
<p>Somethingbecomesthis</p>
Чтобы пофиксить эту проблему, вам надо явно добавить пробел, добавив выражения как тут:
<p>
Something
{' '}becomes
{' '}this
</p>
Или вставив строку в выражении:
<p>
Something
{' becomes '}
this
</p>
Добавление комментариев в JSX
Вы можете добавлять комментарии в JSX, используя обычные JavaScript комментарии внутри выражения:
<p>
{/* коммент */}
{
//еще один коммент
}
</p>
Атрибуты расширения
В JSX довольно частой операцией является назначение значений атрибутам. Вместо того, чтобы делать это вручную:
<div>
<BlogPost title={data.title} date={data.date} />
</div>
Вы можете передать их с помощью оператора расширения Spread:
<div>
<BlogPost {...data} />
</div>
И свойства из объекта data
, будут использоваться, как атрибуты в автоматическом порядке, спасибо тут оператору расширения из ES2015.
Циклы в JSX
Если у вас есть есть коллекция элементов по которой вам нужно пройтись циклом для создания JSX части компонента, то вы можете спокойно запустить цикл и затем добавить JSX в массив.
const elements = [] //..какой то массив
const items = []
for (const [index, value] of elements.entries()) {
items.push(<Element key={index} />)
}
Теперь отрендеря JSX вы можете вставить массив items
, обернув его в фигурные скобки.
const elements = ['one', 'two', 'three'];
const items = []
for (const [index, value] of elements.entries()) {
items.push(<li key={index}>{value}</li>)
}
return (
<div>
{items}
</div>
)
Вы можете сделать тоже самое напрямую в JSX, применяя map
, вместо for-of
цикла.
const elements = ['one', 'two', 'three'];
return (
<ul>
{elements.map((value, index) => {
return <li key={index}>{value}</li>
})}
</ul>
)