We're live-coding on Twitch! Join us!
Why You Can't Return Parallel JSX Elements. The Hack!

Why You Can't Return Parallel JSX Elements. The Hack!

The Answer

JavaScript as a language doesn’t support multiple return values. In writing React code, your JSX (JavaScript XML) eventually gets transpiled into vanilla JavaScript. When this happens, parallel JSX elements are transpiled into multiple return values which isn’t a feature supported by the language(JavaScript).

A Deeper Dive into Return Statements, JavaScript and JSX

If you’ve written React for quite sometime, you probably have noticed an error of this sort that occurs whenever you try to return more than one element from a React component i,e

import React from 'react'

const Loading = () => {
  return (
    <p>Another parent element</p>
    <div className="text-center">
      <i className="fas fa-spinner fa-3x"></i>
      <h4 className="mb-3">This may take a few seconds. Please wait!</h4>
      <p>Loading....</p>
    </div>
  )
}
export default Loading

Why does this occur? And what does it really mean?

To find the answers, let’s briefly examine how return statements work in Javascript.

Return Statements

In JavaScript, the return statement is used to terminate a function’s execution and return a single value from the function. This single value could be a string, number, array, object, function etc.

The only condition is that it must evaluate to a value. Otherwise undefined is return from the function.

Here’s a typical example:

Essential Reading: Learn React from Scratch! (2019 Edition)
function sum(a, b) {
  return a + b
}

More information about return statements can be found here on the Mozilla Developer Network.

Notice that a return statement terminates the execution of a function. This makes code as shown below flawed as the latter statement can never be reached once the first return statement is executed.

function sumAndDifference(a, b) {
  return a + b
  return a - b
}

OR

function sumAndDifference(a, b) {
  return a + b, a - b
}

A typical solution to this problem in JavaScript would be to group return statements into collections that can be de-structured at a later point.

function sumAndDifference(a, b) {
  return [a + b, a - b]
}

OR

function sumAndDifference(a, b) {
  return {
    sum: a + b,
    difference: a - b
  }
}

This behaviour is what is common across many modern programming languages. Although, some programming languages like “Go” have ways of implementing multiple return statements, in many cases the idea comes down to grouping the items as one unit that can be de-structured when the returned values are needed.

JavaScript meets JSX

When React components created using JSX get transpiled into vanilla JavaScript, we basically end up with functions that return some constructed markup to be injected into the DOM. See a typical example below:

React code

import React from 'react'

const Loading = () => {
  return(
    <div className="text-center">
      <i className="fas fa-spinner fa-3x"></i>
      <h4 className="mb-3">This may take a few seconds. Please wait!</h4>
      <p>Loading....</p>
    </div>
  )
}

export default Loading

Transpiled Vanilla JavaScript

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _react = _interopRequireDefault(require("react"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var Loading = function Loading() {
  return _react.default.createElement("div", {
    className: "text-center"
  }, _react.default.createElement("i", {
    className: "fas fa-spinner fa-3x"
  }), _react.default.createElement("h4", {
    className: "mb-3"
  }, "This may take a few seconds. Please wait!"), _react.default.createElement("p", null, "Loading...."));
};

var _default = Loading;
exports.default = _default;

Notice how the function Loading above returns some markup created from a series of nested elements constructed via React’s createElement method.

A closer look at the JSX transpilation

Let’s now take a closer look at the JSX conversion that took place above:

React.createElement("div", {
  className: "text-center"
}, React.createElement("i", {
  className: "fas fa-spinner fa-3x"
}), React.createElement("h4", {
  className: "mb-3"
}, "This may take a few seconds. Please wait!"), React.createElement("p", null, "Loading...."));

Without all the other things that make up a React component, the JSX earlier written translates into the code above. As the React documentation clearly states:

createElement() creates and returns a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), a React component type (a class or a function), or a React fragment type.

It accepts parameters as shown below:

React.createElement(
  type,
  [props],
  [...children]
)

At conversion, this method is used to recreate the markup originally written in JSX, and then the markup is returned from the function. Notice that in the example above, there is only one enclosing element returned from the React component’s render function. Thus, the transpiled JSX has only one return value which nests every other element enclosed by the parent element.

What happens when there is more than one enclosing element? When there is more than one enclosing element, the transpiled function tends to have two return values(one for each enclosing element). See example below:

React Code

import React from 'react'

const Loading = () => {
  return(
    <p>Another parent element</p>
    <div className="text-center">
      <i className="fas fa-spinner fa-3x"></i>
      <h4 className="mb-3">This may take a few seconds. Please wait!</h4>
      <p>Loading....</p>
    </div>
  )
}
export default Loading

Transpiled Vanilla JavsScript

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _react = _interopRequireDefault(require("react"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var Loading = function Loading() {
  return _react.createElement("p", {}, "Another parent element"),
  _react.default.createElement("div", {
    className: "text-center"
  }, _react.default.createElement("i", {
    className: "fas fa-spinner fa-3x"
  }), _react.default.createElement("h4", {
    className: "mb-3"
  }, "This may take a few seconds. Please wait!"), _react.default.createElement("p", null, "Loading...."));
};

var _default = Loading;
exports.default = _default;

Notice that in the transpiled code, the function Loading now has multiple return values in the format return a,b.

This would result in an error as this is unsupported in JavaScript, thus unsupported in JSX(JavaScript XML).

Here’s the Solution to working with Parallel JSX Elements

Recall that with vanilla JS, solving this problem would require that you group the statements to be returned into a collection of some sort so that it is treated as one value.

1 . Wrapping all markup in an extra element An ideal way to do this in JSX would be to group the elements, by adding an extra wrapping element that encloses all markup. See example below:

import React from 'react'

const Loading = () => {
  return (
    <div>
      <p>Another parent element</p>
      <div className="text-center">
        <i className="fas fa-spinner fa-3x"></i>
        <h4 className="mb-3">This may take a few seconds. Please wait!</h4>
        <p>Loading....</p>
      </div>
    </div>
  )
}

export default Loading

As much as this solves the problem, in some cases you can’t afford to add an extra node to the DOM as it may mess up your styling or page layout. This leads us to the second solution.

2. Using React Fragment React Fragment is used to group a list of child elements without adding extra nodes to the DOM. See example below:

import React, { Fragment } from 'react'

const Loading = () => {
  return (
    <Fragment>
      <p>Another parent element</p>
      <div className="text-center">
        <i className="fas fa-spinner fa-3x"></i>
        <h4 className="mb-3">This may take a few seconds. Please wait!</h4>
        <p>Loading....</p>
      </div>
    </Fragment>
  )
}

export default Loading

This creates a container element such that when the code is transpiled it is wrapped within a React fragment which does not add an extra node on injection into the DOM. It has a shorthand syntax which makes use of empty tags as shown below:

import React from 'react'

const Loading = () => {
  return (
    <>
      <p>Another parent element</p>
      <div className="text-center">
        <i className="fas fa-spinner fa-3x"></i>
        <h4 className="mb-3">This may take a few seconds. Please wait!</h4>
        <p>Loading....</p>
      </div>
    </>
  )
}

export default Loading

Conclusion

Here’s what we learned in this article:

  • JavaScript does not support multiple return values.
  • JSX in React is transpiled to vanilla JS which uses React’s createElement() method to compose the markup as outlined using JSX.
  • When you return parallel JSX elements from a Component’s render function in React, the code gets transpiled into a function that returns multiple values which is not supported in JavaScript.
  • To solve this problem, you can either use a wrapping element to enclose all markup or use React Fragments in cases where you cannot afford to inject any extra nodes into the DOM.

Like this article? Follow @worldclassdev on Twitter