3

JSX (react) in depth - an interactive tutorial

 3 years ago
source link: https://blog.klipse.tech/javascript/2016/12/14/jsx.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Prelude

This article is an interactive version of Facebook official tutorial JSX in depth using the KLIPSE plugin to make the code snippets interactive: the JSX code will be transpiled in your browser while you read this article and a couple of react components will be rendered.

I hope that you will enjoy the code interactivity…

Fundamentally, JSX just provides syntactic sugar for the React.createElement(component, props, ...children) function.

Let’s see how a simple piece of JSX code is compiled:

xxxxxxxxxx
<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>
the evaluation will appear here (soon)...

You can also use the self-closing form of the tag if there are no children:

xxxxxxxxxx
<div className="sidebar" />
xxxxxxxxxx
the evaluation will appear here (soon)...

If you want to test out how some specific JSX is converted into JavaScript, you can try out the online Babel compiler or simply edit the code snippets of this page: a babel transpiler has been embedded into this page using the KLIPSE plugin.

(By the way, I’m the author of the KLIPSE plugin. If you like the interactive code snippets, please give KLIPSE a star on github and integrate the plugin into your blog or your slides; it’s just a javascript tag that provides interactive code snippets in a couple of languages. The full instructions are on KLIPSE github repo.)

A small thing that is quite cool with KLIPSE is that you can interactively edit React JSX components and see how they are rendered - as you type…

xxxxxxxxxx
function MyButton(props) {
  return (
    <div>
      <button style={{color: props.color}} >Click Me</button>
    </div>);
}
ReactDOM.render(React.createElement(MyButton, {color: "green"}), container);
xxxxxxxxxx
the evaluation will appear here (soon)...

Go ahead, modify the above code snippet and see how it is rendered as you type.

And if you are really proud of your work, share a screenshot with the code and the rendered component on twitter to @viebel mentionning #klipse.

Specifying The React Element Type

The first part of a JSX tag determines the type of the React element.

Capitalized types indicate that the JSX tag is referring to a React component. These tags get compiled into a direct reference to the named variable, so if you use the JSX <Foo /> expression, Foo must be in scope.

React Must Be in Scope

Since JSX compiles into calls to React.createElement, the React library must also always be in scope from your JSX code.

For example, both of the imports are necessary in this code, even though React and CustomButton are not directly referenced from JavaScript:

xxxxxxxxxx
import React from 'react';
import CustomButton from './CustomButton';
function WarningButton() {
  return <CustomButton color="red" />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

If you don’t use a JavaScript bundler and added React as a script tag (like in this blog post) , it is already in scope as a React global.

xxxxxxxxxx
function WarningButton() {
  return <CustomButton color="red" />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Using Dot Notation for JSX Type

You can also refer to a React component using dot-notation from within JSX. This is convenient if you have a single module that exports many React components. For example, if MyComponents.DatePicker is a component, you can use it directly from JSX with:

xxxxxxxxxx
const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}
function BlueDatePicker() {
  return <MyComponents.DatePicker color="blue" />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

User-Defined Components Must Be Capitalized

When an element type starts with a lowercase letter, it refers to a built-in component like <div> or <span> and results in a string 'div' or 'span' passed to React.createElement. Types that start with a capital letter like <Foo /> compile to React.createElement(Foo) and correspond to a component defined or imported in your JavaScript file.

We recommend naming components with a capital letter. If you do have a component that starts with a lowercase letter, assign it to a capitalized variable before using it in JSX.

For example, this code will not run as expected as hello is a component and should have been capitalized. React thinks is an HTML tag because it’s not capitalized:

xxxxxxxxxx
function hello(props) {
  return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
  return <hello toWhat="World" />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

If we try to render HelloWorld, nothing is rendered and if you open your browser console you’ll see an error message:

xxxxxxxxxx
ReactDOM.render(React.createElement(HelloWorld, null), container);
xxxxxxxxxx
the evaluation will appear here (soon)...

To fix this, we will rename hello to Hello and use <Hello /> when referring to it:

(The use of <div> is legitimate because 'div' is a valid HTML tag.)

xxxxxxxxxx
function Hello(props) {
  return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
  return <Hello toWhat="World" />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Now, we can render HelloWorld properly:

xxxxxxxxxx
function Hello(props) {
  return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {
  return <Hello toWhat="World" />;
}
ReactDOM.render(React.createElement(HelloWorld, null), container);
xxxxxxxxxx
the evaluation will appear here (soon)...

Choosing the Type at Runtime

You cannot use a general expression as the React element type. If you do want to use a general expression to indicate the type of the element, just assign it to a capitalized variable first. This often comes up when you want to render a different component based on a prop.

This won’t work:

xxxxxxxxxx
const components = {
  photo: PhotoStory,
  video: VideoStory
};
function Story(props) {
  return <components[props.storyType] story={props.story} />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

To fix this, we will assign the type to a capitalized variable first:

xxxxxxxxxx
const components = {
  photo: PhotoStory,
  video: VideoStory
};
function Story(props) {
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Props in JSX

There are several different ways to specify props in JSX.

JavaScript Expressions

You can pass any JavaScript expression as a prop, by surrounding it with {}. For example, in this JSX:

xxxxxxxxxx
<MyComponent foo={1 + 2 + 3 + 4} />
xxxxxxxxxx
the evaluation will appear here (soon)...

For MyComponent, the value of props.foo will be 10 because the expression 1 + 2 + 3 + 4 gets evaluated.

if statements and for loops are not expressions in JavaScript, so they can’t be used in JSX directly.

xxxxxxxxxx
<MyComponent foo={if(true) {1}} />
xxxxxxxxxx
the evaluation will appear here (soon)...

Instead, you can put these in the surrounding code. For example:

xxxxxxxxxx
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>;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

String Literals

You can pass a string literal as a prop. These two JSX expressions are equivalent:

xxxxxxxxxx
<MyComponent message="hello world" />
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<MyComponent message={'hello world'} />
xxxxxxxxxx
the evaluation will appear here (soon)...

When you pass a string literal, its value is HTML-unescaped. So these two JSX expressions are equivalent:

xxxxxxxxxx
<MyComponent message="<3" />
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<MyComponent message={'<3'} />
xxxxxxxxxx
the evaluation will appear here (soon)...

This behavior is usually not relevant. It’s only mentioned here for completeness.

Props Default to “True”

If you pass no value for a prop, it defaults to true. These two JSX expressions are equivalent:

xxxxxxxxxx
<MyTextBox autocomplete />
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<MyTextBox autocomplete={true} />
xxxxxxxxxx
the evaluation will appear here (soon)...

In general, we don’t recommend using this because it can be confused with the ES6 object shorthand {foo} which is short for {foo: foo} rather than {foo: true}. This behavior is just there so that it matches the behavior of HTML.

Spread Attributes

If you already have props as an object, and you want to pass it in JSX, you can use ... as a “spread” operator to pass the whole props object. These two components are equivalent:

xxxxxxxxxx
function App1() {
  return <Greeting firstName="Ben" lastName="Hector" />;
}
function App2() {
  const props = {firstName: 'Ben', lastName: 'Hector'};
  return <Greeting {...props} />;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Spread attributes can be useful when you are building generic containers. However, they can also make your code messy by making it easy to pass a lot of irrelevant props to components that don’t care about them. We recommend that you use this syntax sparingly.

Children in JSX

In JSX, expressions that contain both an opening tag and a closing tag, the content between those tags is passed as a special prop: props.children. There are several different ways to pass children:

String Literals

You can put a string between the opening and closing tags and props.children will just be that string. This is useful for many of the built-in HTML elements. For example:

xxxxxxxxxx
<MyComponent>Hello world!</MyComponent>
xxxxxxxxxx
the evaluation will appear here (soon)...

This is valid JSX, and props.children in MyComponent will simply be the string "Hello world!". HTML is unescaped, so you can generally write JSX just like you would write HTML in this way:

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

JSX removes whitespace at the beginning and ending of a line. It also removes blank lines. New lines adjacent to tags are removed; new lines that occur in the middle of string literals are condensed into a single space. So these all render to the same thing:

xxxxxxxxxx
<div>Hello World</div>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div>
  Hello World
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div>
  Hello
  World
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div>
  Hello World
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

JSX Children

You can provide more JSX elements as the children. This is useful for displaying nested components:

xxxxxxxxxx
<MyContainer>
  <MyFirstComponent />
  <MySecondComponent />
</MyContainer>
xxxxxxxxxx
the evaluation will appear here (soon)...

You can mix together different types of children, so you can use string literals together with JSX children. This is another way in which JSX is like HTML, so that this is both valid JSX and valid HTML:

xxxxxxxxxx
<div>
  Here is a list:
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
  </ul>
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

A React component can’t return multiple React elements, but a single JSX expression can have multiple children, so if you want a component to render multiple things you can wrap it in a div like this.

JavaScript Expressions

You can pass any JavaScript expression as children, by enclosing it within {}. For example, these expressions are equivalent:

xxxxxxxxxx
<MyComponent>foo</MyComponent>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<MyComponent>{'foo'}</MyComponent>
xxxxxxxxxx
the evaluation will appear here (soon)...

This is often useful for rendering a list of JSX expressions of arbitrary length. For example, this renders an HTML list:

xxxxxxxxxx
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>
  );
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Let’s render it:

xxxxxxxxxx
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>
  );
}
ReactDOM.render(React.createElement(TodoList, null), container);
xxxxxxxxxx
the evaluation will appear here (soon)...

JavaScript expressions can be mixed with other types of children. This is often useful in lieu of string templates:

xxxxxxxxxx
function Hello(props) {
  return <div>Hello {props.addressee}!</div>;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Functions as Children

Normally, JavaScript expressions inserted in JSX will evaluate to a string, a React element, or a list of those things. However, props.children works just like any other prop in that it can pass any sort of data, not just the sorts that React knows how to render. For example, if you have a custom component, you could have it take a callback as props.children:

xxxxxxxxxx
function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>}
    </Repeat>
  );
}
// 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>;
}
xxxxxxxxxx
the evaluation will appear here (soon)...

Children passed to a custom component can be anything, as long as that component transforms them into something React can understand before rendering. This usage is not common, but it works if you want to stretch what JSX is capable of.

Booleans, Null, and Undefined Are Ignored

false, null, undefined, and true are valid children. They simply don’t render. These JSX expressions will all render to the same thing:

xxxxxxxxxx
<div />
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div></div>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div>{false}</div>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div>{null}</div>
xxxxxxxxxx
the evaluation will appear here (soon)...
xxxxxxxxxx
<div>{true}</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

This can be useful to conditionally render React elements. This JSX only renders a <Header /> if showHeader is true:

xxxxxxxxxx
<div>
  {showHeader && <Header />}
  <Content />
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

One caveat is that some “falsy” values, such as the 0 number, are still rendered by React. For example, this code will not behave as you might expect because 0 will be printed when props.messages is an empty array:

xxxxxxxxxx
<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

To fix this, make sure that the expression before && is always boolean:

xxxxxxxxxx
<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

Conversely, if you want a value like false, true, null, or undefined to appear in the output, you have to convert it to a string first:

xxxxxxxxxx
<div>
  My JavaScript variable is {String(myVariable)}.
</div>
xxxxxxxxxx
the evaluation will appear here (soon)...

As I wrote earlier, I’m the author of the KLIPSE plugin. If you like the interactive code snippets, please give KLIPSE a star on github and integrate the plugin into your blog or your slides; it’s just a javascript tag that provides interactive code snippets in a couple of languages. The full instructions are on KLIPSE github repo.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK