JSX в деталях¶
JSX — синтаксический сахар для функции React.createElement(component, props, ...children). Этот JSX-код:
1 2 3 | |
Скомпилируется в:
1 2 3 4 5 | |
Вы также можете использовать самозакрывающийся тег, если отсутствуют дочерние элементы. Поэтому код:
1 | |
Скомпилируется в:
1 | |
Указание типа React-элемента¶
Первая часть JSX тега определяет тип React-элемента.
Типы, написанные с большой буквы, указывают, что JSX-тег ссылается на React-компонент. Эти теги компилируются в прямую ссылку на именованную переменную, поэтому, если вы используете JSX-выражение <Foo />, то Foo должен быть в области видимости.
React должен находиться в области видимости¶
Поскольку JSX компилируется в вызов React.createElement, библиотека React должна всегда быть в области видимости вашего JSX-кода.
К примеру, в данном коде оба импорта являются необходимыми, даже если на React и CustomButton нет прямых ссылок из JavaScript:
1 2 3 4 5 6 7 | |
Если вы не используете сборщик JavaScript и загружаете React с помощью тега <script>, то он уже доступен как React в глобальной области видимости.
Использование записи через точку¶
Вы также можете ссылаться на React-компонент, используя запись через точку. Это удобно, если у вас есть модуль, который экспортирует много React-компонентов. К примеру, если MyComponents.DatePicker является компонентом, то вы можете обратиться к нему напрямую, используя запись через точку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Названия типов пользовательских компонентов должны начинаться с большой буквы¶
Если название типа элемента начинается с маленькой буквы, он ссылается на встроенный компонент, к примеру <div> или <span>, что в результате приведёт к тому, что в React.createElement будет передана строка 'div' или 'span'. Типы, начинающиеся с заглавной буквы, такие как <Foo />, компилируются в React.createElement(Foo) и соответствуют компоненту, который был объявлен или импортирован в вашем JavaScript-файле.
Мы рекомендуем называть компоненты с заглавной буквы. Если у вас есть компонент, название которого начинается с маленькой буквы, то перед тем как использовать его в JSX, присвойте его в переменную, которая имеет название с заглавной буквы.
К примеру, этот код будет работать не так, как ожидается:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Для того, чтобы исправить это, мы переименуем hello в Hello и станем использовать <Hello />, когда будем ссылаться на него:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Выбор типа во время исполнения¶
В качестве типа React-элемента нельзя использовать выражение. Если вы хотите использовать выражение, чтобы указать тип элемента, присвойте его в переменную, начинающуюся с заглавной буквы. Это подходит для рендера компонентов в зависимости от ваших пропсов:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Чтобы исправить это, мы присвоим тип в переменную, начинающуюся с заглавной буквы:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Пропсы в JSX¶
Существует несколько разных способов передачи пропсов в JSX.
JavaScript-выражения как пропсы¶
Вы можете передавать любые JavaScript-выражения как пропсы, обернув их в {}. К примеру, в этом JSX:
1 | |
Для MyComponent значение props.foo будет равно 10, потому что выражение 1 + 2 + 3 + 4 будет вычислено.
Оператор if и цикл for не являются выражениями в JavaScript, поэтому их нельзя непосредственно использовать в JSX. Вместо этого, вы можете окружить ими JSX-код. К примеру:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Вы можете узнать больше про условный рендеринг и циклы в соответствующих разделах.
Строковые литералы¶
Вы можете передать строковый литерал как проп. Эти два выражения эквивалентны:
1 2 3 | |
Когда вы передаёте строковый литерал, все его возможные символы будут преобразованы в соответствующие HTML-сущности. Поэтому эти два JSX-выражения будут эквивалентны:
1 2 3 | |
Обычно такое поведение не должно вас волновать. Оно упомянуто для полноты картины.
Установка пропсов по умолчанию в «true»¶
Если вы не передаёте значение в проп, то по умолчанию оно будет true. Эти два JSX выражения эквивалентны:
1 2 3 | |
В основном, мы не рекомендуем так делать, потому что это может быть воспринято как сокращение имён свойств из ES6. Тогда, например, {foo} это короткая запись {foo: foo}, но никак не {foo: true}. Такое поведение существует для того, чтобы соответствовать поведению HTML.
Атрибуты расширения¶
Если у вас уже есть пропсы внутри объекта props и вы хотите передать их в JSX, вы можете использовать оператор расширения .... Эти два компонента эквивалентны:
1 2 3 4 5 6 7 8 | |
Вы так же можете выбрать конкретные пропсы, которые ваш компонент будет использовать, передавая все остальные пропсы с помощью оператора расширения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
В приведённом выше примере, проп kind используется безопасно и не передаётся в элемент <button>, находящийся в DOM. Все остальные пропсы передаются с помощью объекта ...other, что делает этот компонент очень гибким. Вы можете видеть, что он передаёт пропсы onClick и children.
Атрибуты расширения могут быть полезны, однако, также они позволяют передать ненужные пропсы в компоненты или невалидные HTML-атрибуты в DOM. Мы рекомендуем использовать этот синтаксис с осторожностью.
Дочерние компоненты в JSX¶
В JSX-выражениях содержимое, которое расположено между открывающими и закрывающими тегами, передаётся с помощью специального пропа: props.children. Существует несколько способов передать дочерние компоненты:
Строковые литералы¶
Если вы поместите строку между открывающим и закрывающим тегом, то props.children будет равно этой строке. Это полезно при создании встроенных HTML-элементов. К примеру:
1 | |
Это корректный JSX-код, в котором значение props.children в MyComponent будет строкой "Привет, мир!". HTML не экранируется, поэтому JSX можно писать также как HTML:
1 | |
JSX удаляет пустые строки и пробелы в начале и конце строки. Новые строки, примыкающие к тегу будут удалены. Новые строки между строковых литералов сжимаются в один пробел. Следующие три примера кода рендерят одинаковый результат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Дочерние JSX-компоненты¶
Чтобы отобразить вложенные компоненты, можно указать несколько JSX-элементов в качестве дочерних.
1 2 3 4 | |
Вы можете смешивать различные типы потомков, скажем, использовать строковый литерал вместе с JSX-элементами. Вот ещё один пример, в котором JSX похож на HTML, причём данный код является валидным и для JSX, и для HTML:
1 2 3 4 5 6 7 | |
Также React-компонент может возвращать массив элементов:
1 2 3 4 5 6 7 8 9 | |
JavaScript-выражения как дочерние компоненты¶
Вы можете передать любое JavaScript-выражение как дочерний компонент, обернув его в {}. К примеру, эти выражения эквивалентны:
1 2 3 | |
Часто это бывает полезно при рендере списка JSX-выражений произвольной длины. Например, эта запись рендерит HTML-список:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
JavaScript-выражения могут быть использованы вместе с другими типами дочерних компонентов. Они могут рассматриваться как альтернатива шаблонным строкам:
1 2 3 | |
Функции как дочерние компоненты¶
Обычно JavaScript-выражения, вставленные в JSX, будут приведены к строке, React-элементу или списку из всего этого. Тем не менее, props.children работает так же, как и любой другой проп, поэтому в него можно передавать любые типы данных, а не только те, которые React знает как рендерить. К примеру, если у вас есть пользовательский компонент, можно было бы передать колбэк в props.children:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Дочерние компоненты, передаваемые пользовательскому компоненту, могут быть чем угодно с тем условием, что компонент преобразует их во что-то, что React сможет понять и отрендерить. Следующий пример редко встречается, но им можно воспользоваться, если необходимо расширить возможности JSX.
Логические значения, null и undefined игнорируются¶
Значения false, null, undefined и true — валидные дочерние компоненты. Просто они не рендерятся. Эти JSX-выражения будут рендерить одно и то же:
1 2 3 4 5 6 7 8 9 10 11 | |
Этот подход может быть полезным для рендера по условию. Вот пример, где JSX рендерит <Header />, если showHeader равняется true:
1 2 3 4 | |
Есть один нюанс в том, что React будет рендерить «ложные» (falsy) значения, такие как число 0. Код ниже ведёт себя не так, как вы могли ожидать, так как 0 будет отображён, если массив props.messages пуст:
1 2 3 4 5 | |
Чтобы исправить это, убедитесь что выражение перед оператором && всегда является boolean:
1 2 3 4 5 | |
И наоборот, если вы хотите, чтобы такие значения как false, true, null или undefined отрисовались, то сначала вы должны преобразовать их в строку:
1 | |