Getting Started with React (6) Deep into JSX

The main content of this blog is in-depth JSX-related content from the Advanced Guidelines section of the React doc.

What is JSX?

In fact, JSX is just syntactic sugar for the’React.createElement (component, props,… children) 'function. The following JSX code:

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

Will compile as:

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

If there is no sub-node, you can also use self-closing tag forms, such as:

1
<div className="sidebar" />

Will compile as:

1
2
3
4
React.createElement(
'div',
{className: 'sidebar'}
)

That is to say, in fact, the so-called JSX needs to be compiled to generate React code, which is somewhat similar to Vue’s template. Vue’s template will also be compiled to generate a render function. In this function, for each tag, it will call its own API to create vnode.

Specify

The first part of the JSX tag specifies the type of the React element.

JSX tags that start with a capital letter mean they are React components. These tags are compiled as direct references to named variables, so when you use JSX < Foo/> expressions, ‘Foo’ must be included in scope.

React

Since JSX compiles as React.createElement calls, the React library must also be included in the JSX code scope.

For example, in the following code, although’React ‘and’CustomButton’ are not directly used, they still need to be imported:

1
2
3
4
5
import React from 'react';import CustomButton from './CustomButton';
function WarningButton() {
// return React.createElement(CustomButton, {color: 'red'}, null);
return <CustomButton color="red" />;
}

If you load React directly through the < script > tag instead of using a JavaScript packaging tool, you must mount React into a global variable.

In

In JSX, you can also use dot syntax to reference a React component. This is very handy when you export many React components in a module. For example, if’MyComponents. DatePicker 'is a component, you can use it directly in JSX:

1
2
3
4
5
6
7
8
9
10
11
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" />;
}

User-defined components must start with a capital letter

Elements starting with a lowercase letter represent an HTML built-in component, such as’ < div > ‘or’ < span > ‘will generate the corresponding string “div” or “span” to be passed to’React.createElement’ (as a parameter). Elements starting with uppercase letters correspond to components introduced or customized in JavaScript, such as’ < Foo/> 'will compile to’React.createElement (Foo) '.

We recommend naming custom components with uppercase letters. If you do need a component that starts with a lowercase letter, you must assign it to a variable that starts with an uppercase letter before using it in JSX.

For example, the following code will not run as expected.

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';

//Error! Components should start with a capital letter:
function hello(props) {
//Correct! This use of < div > is legal because div is a valid HTML tag
return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
//Error! React will think < hello/> is an HTML tag because it doesn't start with a capital letter:
return <hello toWhat="World" />;
}

To fix this, we need to rename hello to Hello and use < Hello/> in JSX:

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';

//Correct! Components need to start with a capital letter:
function Hello(props) {
//Correct! This use of < div > is legal because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
React knows that < Hello/> is a component because it starts with a capital letter:
return <Hello toWhat="World" />;
}

Select type at runtime

You cannot use a universal expression as a React element type. If you want to (dynamically) determine the element type through a universal expression, you need to first assign it to a variable starting with a capital letter. This is usually used when rendering different components based on prop:

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

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

function Story(props) {
//Error! The JSX type cannot be an expression.
return <components[props.storyType] story={props.story} />;
}

To solve this problem, we need to first assign the type to a variable starting with a capital letter.

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

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

function Story(props) {
//Correct! JSX types can be variables starting with uppercase letters.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}

JSX

There are several ways to specify props in JSX.

JavaScript

You can pass a JavaScript expression wrapped in ‘{}’ as a prop to a JSX element. For example, JSX like this:

1
<MyComponent foo={1 + 2 + 3 + 4} />

In MyComponent, the value of props.foo is equal to the result of 1 + 2 + 3 + 4.

'If ‘statements and’for’ loops are not JavaScript expressions, so they cannot be used directly in JSX. However, you can use them in code other than JSX. For example:

1
2
3
4
5
6
7
8
9
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>;
}

You can learn more about it in the corresponding chapters条件渲染And循环The content.

String literal

You can assign string literals to prop. The following two JSX expressions are equivalent:

1
2
3
<MyComponent message="hello world" />

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

When you assign a string literal to a prop, its value is unescaped. Therefore, the following two JSX expressions are equivalent:

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

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

This behavior is usually unimportant, here is just a reminder of this usage.

Props

If you do not assign a value to prop, its default value is’true '. The following two JSX expressions are equivalent:

1
2
3
<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

Generally, we do not recommend not passing value to prop, as this may be related to ES6 对象简写Confused, ‘{foo}’ is short for ‘{foo: foo}’, not ‘{foo: true}’. This implementation is only to maintain the same behavior as tag attributes in HTML.

Attribute expansion

If you already have a props object, you can use the expansion operator… to pass the entire props object in JSX. The following two components are equivalent:

1
2
3
4
5
6
7
8
function App1() {
return <Greeting firstName="Ben" lastName="Hector" />;
}

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

You can also choose to keep only the props that the current component needs to receive, and use the expansion operator to pass other props down.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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>
);
};

In the above example, the prop of’kind ‘will be safely kept and will * not * be passed to the’ < button > ‘element in the DOM. All other props will be passed through the’… other ‘object, making the application of this component very flexible. You can see that it passes an’onClick’ and’children 'attribute.

Attribute expansion is useful in some cases, but it is also easy to pass unnecessary props to unrelated components or invalid HTML attributes to the DOM. We recommend using this syntax with caution.

JSX

The content of the JSX expression contained between the start and end tags will be passed to the outer component as a specific attribute’props.children '. There are several different ways to pass child elements:

String literal

You can put the string between the start and end tags, where’props.children 'is just the string. This is useful for many built-in HTML elements. For example:

1
<MyComponent>Hello world!</MyComponent>

This is a valid JSX, and the props.children in MyComponent is a simple non-Escape Character string “Hello world!”. So you can write JSX the same way you write HTML. As shown below:

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

JSX will remove spaces at the beginning and end of the line as well as blank lines. Empty lines adjacent to the label will be deleted, and new lines between text strings will be compressed into one space. Therefore, the following methods are equivalent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div>Hello World</div>

<div>
Hello World
</div>

<div>
Hello
World
</div>

<div>

Hello World
</div>

JSX

Child elements can be composed of multiple JSX elements. This is very useful for nested components.

1
2
3
4
<MyContainer>
<MyFirstComponent />
<MySecondComponent />
</MyContainer>

You can mix different types of child elements together, so you can use string literals with JSX child elements. This is also a manifestation of JSX similar to HTML, so the following code is legal JSX and also legal HTML:

1
2
3
4
5
6
7
<div>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>

React components can also return a set of elements stored in an array:

1
2
3
4
5
6
7
8
9
render() {
No need to wrap the list elements with additional elements!
return [
Don't forget to set the key:)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}

JavaScript

JavaScript expressions can be wrapped in ‘{}’ as child elements. For example, the following expressions are equivalent:

1
2
3
<MyComponent>foo</MyComponent>

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

This is very useful for displaying lists of arbitrary length. For example, rendering HTML lists:

1
2
3
4
5
6
7
8
9
10
11
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 expressions can also be combined with other types of child elements. This approach can conveniently replace template strings.

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

Function as a child element

Usually, JavaScript expressions in JSX will be evaluated as strings, React elements, or lists. However, ‘props.children’, like other props, can pass any type of data, not just the renderable types known to React. For example, if you have a custom component, you can pass the callback function as’props.children ':

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Call the child element callback numTimes times to repeatedly generate the 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>
);
}

You can pass anything as a child element to a custom component, just make sure it can be converted into an object that React understands before rendering it. This usage is not common, but can be used to extend JSX.

Boolean type, Null

'False ‘,’ null ‘,’ undefined ‘, and’true’ are valid child elements. But they will not be rendered. The following JSX expressions render the same:

1
2
3
4
5
6
7
8
9
10
11
<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

This helps to render other React elements based on specific conditions. For example, in the following JSX, the < Header/> component is rendered only when’showHeader ‘is’true’:

1
2
3
<div>
{showHeader && <Header />} <Content />
</div>

It is worth noting that there are some “falsy” 值, like the number ‘0’, will still be rendered by React. For example, the following code will not work as you expect, because ‘0’ will still be rendered when’props.messages’ is an empty array:

1
2
3
4
<div>
{props.messages.length && <MessageList messages={props.messages} />
}
</div>

To solve this problem, make sure that the expression before ‘& &’ is always a boolean value:

1
2
3
4
<div>
{props.messages.length > 0 && <MessageList messages={props.messages} />
}
</div>

Conversely, if you want to render values such as’false ‘,’ true ‘,’ null ‘,’ undefined ', you need to first convert them转换为字符串

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

Summary

There is nothing to understand in this section, it is all grammatical issues, but there are a few places to pay attention to:

  • React components must be in scope.
  • Names of React components must start with uppercase.
  • The content between component tags will be as prop.children, which can be expressions, functions, strings, etc.
  • It’s worth noting that there are some “falsy” 值, such as the number ‘0’, will still be rendered by React. For example, the following code will not work as you expect, because ‘0’ will still be rendered when’props.messages’ is an empty array: To solve this problem, make sure that the expression before ‘& &’ is always a boolean value: