Featured image of post JSX Fragment Syntax in TypeScript

JSX Fragment Syntax in TypeScript

Overview

TypeScript 2.6 added support for JSX fragments. Within .tsx files, you can now use the new <>...</> syntax to create a fragment.

Motivation Behind JSX Fragments

In React, it’s a common pattern to return multiple elements from a component. For instance, let’s say we want to render multiple list items within the following component:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class List extends React.Component {
  render() {
    return (
      <ul>
        <ListItems />
        <li>Item 3</li>
      </ul>
    );
  }
}

However, in our ListItems component, we cannot simply return multiple adjacent JSX elements like this:

1
2
3
4
5
6
7
8
class ListItems extends React.Component {
  render() {
    return (
      <li>Item 1</li>
      <li>Item 2</li>
    );
  }
}

Adjacent JSX elements must be wrapped in an enclosing element, so we could add a wrapping div element:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class ListItems extends React.Component {
  render() {
    return (
      <div>
        <li>Item 1</li>
        <li>Item 2</li>
      </div>
    );
  }
}

Unfortunately, adding such a wrapper breaks the structure of our list. Our ListItems component currently renders the following DOM elements:

1
2
3
4
5
6
7
<ul>
  <div>
    <li>Item 1</li>
    <li>Item 2</li>
  </div>
  <li>Item 3</li>
</ul>

Note that the <div> doesn’t belong in there. We can get rid of it by using the JSX fragment syntax instead:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class ListItems extends React.Component {
  render() {
    return (
      <>
        <li>Item 1</li>
        <li>Item 2</li>
      </>
    );
  }
}

A fragment lets us group multiple JSX elements without adding an extra wrapper node. Now, our List component renders the expected markup:

1
2
3
4
5
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

Alternatively, we could’ve explicitly written React.Fragment instead of using the new JSX syntax:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class ListItems extends React.Component {
  render() {
    return (
      <React.Fragment>
        <li>Item 1</li>
        <li>Item 2</li>
      </React.Fragment>
    );
  }
}

The two versions of our ListItems component are equivalent and render exactly the same output (given that we compile our JSX for use with React).

Compiling JSX Fragments with TypeScript

Here’s our ListItems component with the new JSX syntax again:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class ListItems extends React.Component {
  render() {
    return (
      <>
        <li>Item 1</li>
        <li>Item 2</li>
      </>
    );
  }
}

If we compile the .tsx file with --jsx react (and --target es2015), the following JavaScript is emitted:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class ListItems extends React.Component {
  render() {
    return React.createElement(
      React.Fragment,
      null,
      React.createElement("li", null, "Item 1"),
      React.createElement("li", null, "Item 2"),
    );
  }
}

The compiler replaces the short fragment syntax by a call to the React.createElement() method and passes it React.Fragment as the first argument.

If we compiled our ListItems component with --jsx preserve (and --target es2015) instead, our JSX would be emitted unchanged, set aside { 留出 } whitespace:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class ListItems extends React.Component {
  render() {
    return (
      <>
        <li>Item 1</li>
        <li>Item 2</li>
      </>
    );
  }
}

References

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy