3.1 JSX изнутри
По существу, JSX просто предоставляет синтаксический сахар для функции
React.createElement(component, props, ...children)
. JSX-код:
<CustomButton isDisabled={false} theme={'success'}>
Нажми на меня!
</CustomButton>
компилируется в:
React.createElement(
CustomButton,
{isDisabled: false, theme: 'success'},
'Нажми на меня!'
)
Вы также можете использовать самозакрывающуюся форму тегов, если у них нет потомков:
<div className="my-class" />
компилируется в:
React.createElement(
'div',
{className: 'my-class'},
null
)
Если вам нужно проверить как указанный вами JSX конвертируется в JS, вы можете попробовать компилятор Babel онлайн .
Первая часть JSX-тега определяет тип React-элемента. Типы записываются с
большой буквы и указывают, что JSX-тег ссылается на React-компонент. Эти теги
компилируются в прямые ссылки на именованные переменные. Поэтому если вы используете
выражение <MyComponent />
, MyComponent
должен находиться в области видимости.
3.1.1.1 React должен находиться в области видимости
После того, как JSX скомпилируется в вызовы React.createElement
, библиотека
React также должна всегда быть в области видимости вашего JSX-кода.
Например, оба импорта необходимы в данном коде, даже если на React
и
Message
нет прямых ссылок из JavaScript:
import React from 'react';
import Message from './Message';
function Warning(props) {
// возвращает React.createElement(Message, {type: Message.DANGER, text: props.text}, null);
return <Message type={Message.DANGER} text={props.text}/>;
}
Если вы не используете JavaScript сборщик и подгружаете React
из <script>
тега, то он уже находится в области видимости как глобальный React
объект.
3.1.1.2 Использование записи через точку "." в JSX
Вы также можете ссылаться на React-компонент используя запись через точку внутри JSX.
Это удобно, если у вас есть единственный модуль, который экспортирует несколько React-компонентов.
К примеру, если MyComponents.Button
– это компонент, вы можете обратиться к
нему напрямую в JSX, используя точку:
import React from 'react';
const MyComponents = {
Button: function Button(props) {
return <button className={props.color} value={props.text} onClick={props.onClick}/>;
}
}
function SuccessButton(props) {
return <MyComponents.Button color="green" value="OK" onClick={props.onClick}/>;
}
3.1.1.3 Названия пользовательских компонентов должны начинаться с большой буквы
Когда название типа элемента начинается с маленькой буквы, он ссылается на встроенный компонент,
такой как <div>
или <span>
, обуславливая передачу
строк “div
” или “span
” в вызов React.createElement
.
Названия типов, которые начинаются с большой буквы, такие как <MyComponent/>
компонент,
компилируется в React.createElement(MyComponent)
и соответствует компоненту, определенному
или импортированному в ваш JavaScript файл.
Рекомендуется именовать компоненты с большой буквы. Если у вас есть компонент, названный с маленькой буквы, присвойте его переменной, названной с большой буквы, перед тем как использовать его в JSX.
К примеру, этот код не будет работать как ожидается:
import React from 'react';
// Неправильно! Это компонент и он должен быть записан с большой буквы:
function message(props) {
// Правильно! Использование <div> разрешено, так как это валидный HTML-тег:
return <div>{props.text}</div>;
}
function HelloWorldMessage() {
// Неправильно! React полагает, что <message /> - это HTML-тег,
// потому что записан с маленькой буквы
return <message text="Hello World!" />;
}
Для того, чтобы это исправить, мы переименуем message в Message
и станем
использовать <Message/>
, когда будем ссылаться на него:
import React from 'react';
// Правильно! Это компонент и он должен быть записан с большой буквы:
function Message(props) {
// Правильно! Использование <div> разрешено, так как это валидный HTML-тег:
return <div>{props.text}</div>;
}
function HelloWorldMessage() {
// Правильно! React полагает, что <message /> - это HTML-тег,
// потому что записан с маленькой буквы
return <Message text="Hello World!" />;
}
3.1.1.4 Выбор типа во время выполнения
Нельзя использовать выражение как тип React-элемента в JSX. Если вы
всё же хотите использовать выражение, чтобы указать тип React-элемента,
сперва назначьте его переменной, названной с большой буквы. Часто это подходит,
когда вам необходимо отрисовать различные компоненты, в зависимости от свойств prop
:
import React from 'react';
import { Image, Video } from './media';
const components = {
image: Image,
video: Video
};
function Media(props) {
// Неправильно! JSX-тип не может являться выражением
return <components[props.mediaType] url={props.url} />;
}
Для того, чтобы это исправить, мы должны предварительно присвоить тип переменной, названной с большой буквы:
import React from 'react';
import { Image, Video } from './media';
const components = {
image: Image,
video: Video
};
function Media(props) {
// Правильно! JSX-тип может являться переменной, названной с большой буквы
const MediaObject = components[props.storyType];
return <MediaObject url={props.url} />;
}
Существует несколько способов указать свойства в JSX.
3.1.2.1 JavaScript выражения как свойства
Вы можете передавать любые JavaScript-выражения как свойства,
заключая их в {}
. К примеру, в этом JSX:
<MyComponent foo={1 + 2 + 3 + 4} />
Для MyComponent
, значение props.foo будет равно 10,
так как выражение 1 + 2 + 3 + 4 будет вычислено.
Оператор if
и цикл for
не являются выражениями в JavaScript, поэтому
они не могут быть использованы в JSX напрямую. Вместо этого, вы
можете их в соседний код. К примеру:
function NumberDescriber(props) {
let description;
if (props.number % 2 == 0) {
description = <strong>even</strong>;
} else {
description = <i>odd</i>;
}
return <div>{props.number} - это {description} число</div>;
}
Вы можете изучить больше об условной отрисовке и циклах в соответствующих разделах.
3.1.2.2 Строковые литералы
Вы можете передавать строковый литерал как свойство. Эти два JSX-выражения эквивалентны:
<MyComponent message="Привет мир!" />
<MyComponent message={'Привет мир!'} />
Когда вы передаёте строковый литерал, его значение не будет HTML экранированным (будет HTML-unescaped). Поэтому следующие два JSX-выражения эквивалентны:
<MyComponent message="<3" />
<MyComponent message={'<3'} />
Такое поведение не является релевантным. Оно здесь упомянуто только для полноты.
3.1.2.3 Установка свойств по умолчанию в true
Если вы не передаете значение в свойство, оно устанавливается по умолчанию в true
.
Следующие два JSX-выражения эквивалентны:
<Modal isShowed />
<Modal isShowed={true} />
В большинстве случаев мы не рекомендуем использовать это, так как это можно спутать
с объектным ES6 сокращением {foo}
, который является сокращенной формой записи {foo: foo}
,
а не {foo: true}
. Такое поведение существует просто для того, чтобы соответствовать поведению HTML.
3.1.2.4 Spread – атрибуты
Если у вас уже есть свойства в виде объекта, и вы ходите передать его в JSX, вы можете
использовать "spread"-оператор ...
(троеточие), чтобы передать объект со свойствами целиком.
Следующие два компонента эквивалентны:
function SuccessMessage() {
return <Message type="success" header="Поздравляем!" text="Вы успешно зарегистрированы"/>;
}
function SuccessMessage() {
const props = {type: 'success', header: 'Поздравляем!', text: 'Вы успешно зарегистрированы'};
return <Message {...props} />;
}
Вы также можете выбрать определенные свойства, которые ваш компонент будет использовать,
передавая все остальные свойства с помощью оператора ...
.
const Button = props => {
const { type, ...other } = props;
const className = type === "primary" ? "PrimaryButton" : "SecondaryButton";
return <button className={className} {...other} />;
};
const App = () => {
return (
<div>
<Button type="primary" onClick={() => console.log("Нажато!")}>
Hello World!
</Button>
</div>
);
};
В приведенном выше примере свойство type
безопасно используется и не передается
элементу <button> в DOM. Все остальные свойства передаются через ...other
объект, делающий данный
компонент очень гибким. Вы можете видеть, что он передает свойства onClick
и children
.
Spread-оператор может быть полезен, когда вы строите контейнеры общего назначения. Тем не менее, данный оператор также может сделать ваш код и более грязным, делая простым передачу множества необязательных свойств в компоненты, которые о них совсем не заботятся. Также он позволяет передавать недопустимые HTML-атрибуты в DOM. Рекомендуется использовать данный синтаксис разумно.
В JSX-выражениях, которые содержат и открывающий, и закрывающий теги,
содержание между этими тегами передается как специальное свойство: props.children
.
Существует несколько различных способов передать потомков:
3.1.3.1 Строковые литералы
Вы можете заключить строку между открывающим и закрывающим тегами, тогда
свойство props.children
будет равно этой строке. Это полезно для многих
встроенных HTML-элементов. К примеру:
<MyComponent>Привет, мир!</MyComponent>
Это валидный JSX, и свойство props.children
в MyComponent
будет просто
строкой «Привет, мир!». HTML будет не экранирован, поэтому вы можете писать на
JSX также, как если бы вы писали на обычном HTML:
<div>Это валидный HTML & JSX одновременно.</div>
JSX удаляет пробелы вначале и конце строки. Он также удаляет пустые строки. Новая строка, прилегающая к тегу будет удалена. Новые строки, находящиеся по середине строковых литералов, сжимаются в единичный пробел. Все это отрисуется в то же самое:
<div>Привет, мир!</div>
<div>
Привет, мир!
</div>
<div>
Привет,
мир!
</div>
<div>
Привет, мир!
</div>
3.1.3.2 JSX-потомки
Вы можете предоставить больше JSX-элементов в качестве потомков. Это полезно для отображения вложенных компонентов:
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>
Вы можете смешивать различные типы потомков, то есть можете использовать строковые литералы вместе с JSX-потомками. Это другой способ, в котором JSX такой же, как HTML, поэтому данный код является и валидным JSX, и валидным HTML:
<div>
Список:
<ol>
<li>Элемент 1</li>
<li>Элемент 2</li>
<li>Элемент 3</li>
</ol>
</div>
Начиная с 16 версии React-компонент может возвращать также и массив элементов:
render() {
// Больше нет необходимости оборачивать список элементов в дополнительный элемент!
return [
// Не забывайте про ключи "key" :)
<li key="A">Первый элемент</li>,
<li key="B">Второй элемент</li>,
<li key="C">Третий элемент</li>,
];
}
3.1.3.3 JavaScript выражения как потомки
Вы можете передавать любое JavaScript выражение как потомок,
заключая его в {}
. Например, эти выражения эквивалентны:
<MyComponent>Привет!</MyComponent>
<MyComponent>{'Привет!'}</MyComponent>
Это часто бывает полезным для отрисовки списка JSX-выражений произвольной длинны. Например, здесь отрисовывается HTML-список:
function User(props) {
return <li>{props.user.name}</li>;
}
function UserList() {
const users = [{id: 1, name: 'Вася'}, {id: 2, name: 'Петя'}];
return (
<ul>
{users.map((user) => <User key={user.id} user={user} />)}
</ul>
);
}
JavaScript выражения могут быть смешаны с другими типами потомков. Это часто удобнее использовать, чем строковые шаблоны:
function WarningMessage(props) {
return <div>Внимание! {props.text}</div>;
}
3.1.3.4 Функции как потомки
Как правило JavaScript выражения, вставленные в JSX, будут приведены к строке,
элементу React или списку этих вещей. Тем не менее, props.children
работает точно
также, как и любое другое свойство, так как в него можно передать любой вид данных,
а не только тот вид, который React знает как отрисовать. Например, если у вас есть
пользовательский компонент, вы могли бы передать функцию обратного вызова как props.children
.
// Вызывает коллбэк потомка, чтобы создать повторяемый компонент
function UserList(props) {
return (
<ul>
{props.users.map((user) => props.children(user))}
</ul>
)
}
function UserPage() {
const users = [{id: 1, name: 'Вася'}, {id: 2, name: 'Петя'}];
return (
<UserList users={users}>
{(user) => <li key={user.id}>Пользователь: {user.name}</li>}
</UserList>
);
}
Потомки, переданные в пользовательский компонент, могут быть чем угодно, до тех пор пока компонент трансформирует их во что-нибудь, что React может понимать перед отрисовкой. Это не типичный случай использования и представлен только для того, чтобы вы познали, на что способен JSX.
3.1.3.5 Booleans, Null и Undefined игнорируются
false
, null
, undefined
, и true
– валидные потомки,
но они не отрисовываются. Эти JSX-выражения будут отрисованы одинаково:
<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>
Это может оказаться полезным, чтобы отрисовать элементы React по условию.
Этот JSX отрисовывает <Modal />
только
если isModalShowed
имеет значение true
:
<div>
{isModalShowed && <Modal content={content}/>}
</div>
Один нюанс заключается в том, что “ложные” значения, такие как число 0
,
будут по-прежнему отрисовываться React. К примеру, данный код будет вести себя не так,
как вы могли ожидать, так как будет отрисован 0
, когда props.users
является пустым массивом:
<div>
{props.users.length &&
<UserList users={props.users} />
}
</div>
Чтобы это исправить, убедитесь, что выражение перед
&&
всегда является boolean
:
<div>
{props.users.length > 0 &&
<UserList users={props.users} />
}
</div>
И напротив, если вам нужно значение, такое как false
,
true
, null
, или undefined
, чтобы
вывести его, то тогда вы сперва должны
конвертировать его в строку
:
<div>
Моя JavaScript переменная имеет значение: {String(myVar)}.
</div>