1

When we return something in JSX, we could write:

  <ul>
    {Array.from({ length: 9 }).map((e, i) => (
      <li>{i}</li>
    ))}
  </ul>

or using an IIFE:

  <ul>
    {(function () {
      const result = [];
      for (let i = 0; i < 9; i++) {
        result.push(<li>{i}</li>);
      }
      return result;
    })()}
  </ul>

Example: https://codesandbox.io/s/busy-aryabhata-egv27

But is it possible to write something like:

  <ul>
    {
      const result = [];
      for (let i = 0; i < 9; i++) {
        result.push(<li>{i}</li>);
      }
      return result;  // somehow "return" the array
    }
  </ul>

which is, just write plain code without an IIFE, and is able to somehow return the array we have created?

nonopolarity
  • 146,324
  • 131
  • 460
  • 740
  • Does this answer your question? [Why can you only use expressions in JSX, not statements?](https://stackoverflow.com/questions/60988649/why-can-you-only-use-expressions-in-jsx-not-statements) – jonrsharpe Mar 03 '21 at 12:18

3 Answers3

5

You could pull the for loop up above your JSX, as in:

const result = [];
for (let i = 0; i < 9; i++) {
  result.push(<li>{i}</li>);
}

return (
  <ul>
    {result}
  </ul>
)

But if you want to embed this code inside of JSX, then you can only use expressions, not statements. That's the reason for the IIFE in your second example: it's an expression and so you can put it in JSX, but it can contain statements, thus letting you kind of cheat.

It may help to remember that JSX gets transformed into a function call (or a series of them). For example, this:

<ul>{something}</ul>

becomes this:

React.createElement("ul", null, something);

Looking at that function version, it should be obvious that the following code isn't legal syntax:

React.createElement(
  "ul",
  null,
  const result = [];
  for (let i = 0; i < 9; i++) {
    result.push(<li>{i}</li>);
  }
  return result;
)

And that's why the JSX won't let you do it either.

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • The explanation of JSX becoming `React.createElement("ul", null, something);` and then becoming the final form makes it crystal clear of what's going on. Now I understand why we can put one element there or put an array there and JSX can handle it – nonopolarity Feb 19 '21 at 07:54
3

As you've noted, JSX supports arbitrary JavaScript expressions. Emphasis on the word expressions. An IIFE works because it is a function defining expression and a call expression. There isn't support for your latter example, because JSX doesn't have a mechanism for that would support blocks instead of expressions, and JavaScript doesn't have a mechanism for treating a block as an expression, other than via an IIFE.

However, there is a stage 1 proposal for do expressions, which, if I've understood correctly, would let you do:

<ul>
  {
    do {
      const result = [];
      for (let i = 0; i < 9; i++) {
        result.push(<li>{i}</li>);
      }
      result;
    }
  }
</ul>

It is a stage 1 proposal so it isn't in the standard yet, and may or may not become standard. Luckily, there is a Babel plugin for it, which you might be interested in trying it out: @babel/plugin-proposal-do-expressions

cbr
  • 12,563
  • 3
  • 38
  • 63
1

Not as written. JSX allows only expressions, i.e. code that returns a value. You can write "non-returning" code inside of code that returns other code; you can assign consts inside a map, for example. So if you really wanted to write the above, you could wrap it in a map:

  <ul>
    {
      [0].map(() => {
        const result = [];
        for (let i = 0; i < 9; i++) {
          result.push(<li>{i}</li>);
        }
        return result;
      });
    }
  </ul>

This feels like a thought experiment to me, and probably not a request for best practice. In my mind, that would be to build the array of values outside the JSX, and map over it inside.

const YourComponent = () => {
  ...
  const numbers = Array.from({ length: 9 });

  return(
    <ul>
      { numbers.map((_, i) => <li>{i}</li>) }
    </ul>
  );
Abe
  • 4,500
  • 2
  • 11
  • 25