Unetway

ReactJS - JSX In Depth

По сути, JSX просто предоставляет синтаксический сахар для этой React.createElement(component, props, ...children) функции. Код JSX:

<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

компилируется в:

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

Вы также можете использовать самозакрывающуюся форму тега, если нет детей. Так:

<div className="sidebar" />

компилируется в:

React.createElement(
  'div',
  {className: 'sidebar'},
  null
)

Указание типа элемента реакции

Первая часть тега JSX определяет тип элемента React.

Обозначенные типы указывают, что тег JSX относится к компоненту React. Эти теги скомпилируются в прямую ссылку на именованную переменную, поэтому, если вы используете <Foo /> выражение JSX , оно Fooдолжно быть в области видимости.

Реакция должна быть в области видимости

Поскольку JSX компилируется в вызовы React.createElement, React библиотека также должна всегда находиться в области видимости от вашего кода JSX. Например, оба импорта необходимы в этом коде, хотя Reactи CustomButton не имеют прямой ссылки на JavaScript:

import React from 'react';
import CustomButton from './CustomButton';

function WarningButton() {
  // return React.createElement(CustomButton, {color: 'red'}, null);
  return <CustomButton color="red" />;
}

Если вы не используете пакет JavaScript и загрузили React из <script>тега, он уже входит в область действия как Reactглобальный.

Использование точечной нотации для типа JSX

Вы также можете обратиться к компоненту React с помощью точечной нотации из JSX. Это удобно, если у вас есть один модуль, который экспортирует многие компоненты React. Например, если MyComponents.DatePicker это компонент, вы можете использовать его непосредственно из JSX с:

import React from 'react';

const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}

function BlueDatePicker() {
  return <MyComponents.DatePicker color="blue" />;
}

Пользовательские компоненты должны быть капитализированы

Когда тип элемента начинается с буквы в нижнем регистре, он ссылается на встроенный компонент, такой как <div>или, <span> и приводит к строке 'div' или 'span' передается React.createElement. Типы, начинающиеся с заглавной буквы, такие как <Foo /> компиляция React.createElement(Foo) и соответствие компоненту, определенному или импортированному в ваш файл JavaScript.

Мы рекомендуем назначать компоненты с большой буквы. Если у вас есть компонент, начинающийся с буквы в нижнем регистре, назначьте его капитализированной переменной, прежде чем использовать его в JSX.

Например, этот код не будет работать, как ожидалось:

import React from 'react';

// Wrong! This is a component and should have been capitalized:
function hello(props) {
  // Correct! This use of <div> is legitimate because div is a valid HTML tag:
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  // Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
  return <hello toWhat="World" />;
}

Чтобы это исправить, мы переименуем helloв Helloи использовать <Hello /> при обращении к нему:

import React from 'react';

// Correct! This is a component and should be capitalized:
function Hello(props) {
  // Correct! This use of <div> is legitimate because div is a valid HTML tag:
  return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
  // Correct! React knows <Hello /> is a component because it's capitalized.
  return <Hello toWhat="World" />;
}

Выбор типа во время выполнения

Вы не можете использовать общее выражение в качестве типа элемента React. Если вы хотите использовать общее выражение для указания типа элемента, просто сначала назначьте его капитализированной переменной. Это часто возникает, когда вы хотите отобразить другой компонент на основе prop:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // Wrong! JSX type can't be an expression.
  return <components[props.storyType] story={props.story} />;
}

Чтобы исправить это, мы сначала присвоим тип капитализированной переменной:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // Correct! JSX type can be a capitalized variable.
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

Реквизиты в JSX

Существует несколько способов указать реквизиты в JSX.

Выражения 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} is an {description} number</div>;
}

Строковые литералы

Вы можете передать строковый литерал в качестве опоры. Эти два выражения JSX эквивалентны:

<MyComponent message="hello world" />

<MyComponent message={'hello world'} />

Когда вы передаете строковый литерал, его значение не привязано к HTML. Таким образом, эти два выражения JSX эквивалентны:

<MyComponent message="&lt;3" />

<MyComponent message={'<3'} />

Такое поведение обычно не имеет значения. Это только упоминается здесь для полноты.

Props по умолчанию «True»

Если вы не передадите значение для props, оно по умолчанию true. Эти два выражения JSX эквивалентны:

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

В общем, не рекомендуется использовать это, потому что его можно путать со стенографией объекта ES6, {foo} которая {foo: foo} скорее сокращается, чем {foo: true}. Такое поведение существует именно так, чтобы оно соответствовало поведению HTML.

Атрибуты распространения

Если у вас уже есть props объект, и вы хотите передать его в JSX, вы можете использовать его ...как «распространитель» для передачи всего объекта реквизита. Эти два компонента эквивалентны:

function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}

Вы также можете выбрать определенные реквизиты, которые ваш компонент будет потреблять, передавая все другие реквизиты с помощью оператора распространения.

const Button = props => {
  const { kind, ...other } = props;
  const className = kind === "primary" ? "PrimaryButton" : "SecondaryButton";
  return <button className={className} {...other} />;
};

const App = () => {
  return (
    <div>
      <Button kind="primary" onClick={() => console.log("clicked!")}>
        Hello World!
      </Button>
    </div>
  );
};

В приведенном выше примере kind защитный элемент безопасно потребляется и не передается <button> элементу в DOM. Все остальные реквизиты передаются через ...otherобъект, что делает этот компонент действительно гибким. Вы можете видеть, что он передает onClick и children реквизит.

Атрибуты спреда могут быть полезны, но также облегчают передачу ненужных реквизитов компонентам, которые их не заботятся, или передавать недопустимые HTML-атрибуты в DOM. Мы рекомендуем использовать этот синтаксис экономно.

Потомки в JSX

В выражениях JSX , которые содержат как открывающий тег и закрывающий тег, содержимое между этими тегами передаются в качестве специальной опоры: props.children. Есть несколько способов передачи детей:

Строковые литералы

Вы можете поместить строку между открывающими и закрывающими тегами и props.children просто будет этой строкой. Это полезно для многих встроенных элементов HTML. Например:

<MyComponent>Hello world!</MyComponent>

Это действительный JSX, и props.childrenв нем MyComponent будет просто строка "Hello world!". HTML не привязан, поэтому вы можете вообще писать JSX так же, как и писать HTML таким образом:

<div>This is valid HTML &amp; JSX at the same time.</div>

JSX удаляет пробелы в начале и в конце строки. Он также удаляет пустые строки. Удаляются новые строки, смежные с тегами; новые строки, которые встречаются в середине строковых литералов, сводятся в одно пространство. Таким образом, все они оказывают одно и то же:

<div>Hello World</div>

<div>
  Hello World
</div>

<div>
  Hello
  World
</div>

<div>

  Hello World
</div>

Потомки JSX

Вы можете предоставить больше JSX-элементов в качестве детей. Это полезно для отображения вложенных компонентов:

<MyContainer>
  <MyFirstComponent />
  <MySecondComponent />
</MyContainer>

Вы можете смешивать разные типы детей, поэтому вы можете использовать строковые литералы вместе с детьми JSX. Это еще один способ, в котором JSX похож на HTML, так что это как действительный JSX, так и действительный HTML:

<div>
  Here is a list:
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
  </ul>
</div>

Компонент React также может возвращать массив элементов:

render() {
  // No need to wrap list items in an extra element!
  return [
    // Don't forget the keys :)
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

Выражения JavaScript как потомки

Вы можете передать любое выражение JavaScript в виде дочерних элементов, включив его внутри {}. Например, эти выражения эквивалентны:

<MyComponent>foo</MyComponent>

<MyComponent>{'foo'}</MyComponent>

Это часто полезно для отображения списка выражений JSX произвольной длины. Например, это отображает список HTML:

function Item(props) {
  return <li>{props.message}</li>;
}

function TodoList() {
  const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  return (
    <ul>
      {todos.map((message) => <Item key={message} message={message} />)}
    </ul>
  );
}

Выражения JavaScript могут быть смешаны с другими типами детей. Это часто полезно вместо строковых шаблонов:

function Hello(props) {
  return <div>Hello {props.addressee}!</div>;
}

Функции как потомки

Обычно выражения JavaScript, вставленные в JSX, будут оценивать строку, элемент React или список этих вещей. Тем не менее, props.children работает так же, как и любая другая поддержка в том, что он может передавать любые данные, а не только те виды, которые React знает, как визуализировать. Например, если у вас есть пользовательский компонент, вы можете заставить его выполнить обратный вызов как props.children:

// Calls the children callback numTimes to produce a repeated component
function Repeat(props) {
  let items = [];
  for (let i = 0; i < props.numTimes; i++) {
    items.push(props.children(i));
  }
  return <div>{items}</div>;
}

function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>}
    </Repeat>
  );
}

Дети, переданные в пользовательский компонент, могут быть чем угодно, если этот компонент преобразует их во что-то, что Реакт может понять перед рендерингом. Это использование не является обычным явлением, но оно работает, если вы хотите расширить возможности JSX.

Булевы, Null и Undefined игнорируются

false, null, undefined, И trueявляются действительными дети. Они просто не делают. Эти выражения JSX будут отображать одно и то же:

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

Это может быть полезно для условного рендеринга элементов React. Это JSX только оказывает , <Header /> если showHeader есть true:

<div>
  {showHeader && <Header />}
  <Content />
</div>

Одно из предостережений заключается в том, что некоторые «ложные» значения , такие как 0число, все еще выдаются React. Например, этот код не будет вести себя так, как вы могли бы ожидать, потому что 0 он будет напечатан, когда props.messages будет пустой массив:

<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>

Чтобы исправить это, убедитесь, что выражение before &&всегда имеет значение boolean:

<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }
</div>

И наоборот, если вы хотите значение , как false, true, nullили undefined появляться на выходе, вы должны преобразовать его в строку первой:

<div>
  My JavaScript variable is {String(myVariable)}.
</div>