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 онлайн .


3.1.1 Указание типа React-элемента

Первая часть 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} />;
  }
  


3.1.2 Свойства props в JSX

Существует несколько способов указать свойства в 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. Рекомендуется использовать данный синтаксис разумно.


3.1.3 Потомки в JSX

В 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>