What is the correct way to return map iterator from nested component?

Viewed 147

I constantly end up generating various errors when trying to return (more complex) values from my render method. Most of which, I was able to resolve. But this time, the component doesn't render at all, yet... there is no console error.

I understand the standard return(...) syntax, when it comes to returning single <div/> elements, but in some cases I don't fully understand why it should break my code.

Code examples I'm experimenting with:

class RenderMe1 extends React.Component {
    constructor(props) {
        super(props);
        this.state = { list: ['A','B','C'] }
    }
    render() {
        return(
        <div>
            <div>
                /* === Works === */
                { this.state.list.map((object, index) => this.state.list[index] ) }
            </div>
        </div>);
    }
}

class RenderMe2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = { list: ['0','0','0'] }
    }
    render() {
        return(
        <div>
            <div>
                /* === Doesn't Work === */
                { this.state.list.map((object, index) => { this.state.list[index] } ) }
            </div>
        </div>);
    }
}

ReactDOM.render(<RenderMe1 />, document.getElementById("root"));  // works
ReactDOM.render(<RenderMe2 />, document.getElementById("root2")); // doesn't work

For practice purposes, I'm using in-browser babel plugin, hence the JSX syntax.

3 Answers

Your first example is an implicit return. meaning, you don't need to specify the return keyword because you haven't used curly braces and as such it returns the expression

The second one you have so it isnt returning anything. Here's the amended code

class RenderMe2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = { list: ['0','0','0'] }
    }
    render() {
        return(
        <div>
            <div>

                { this.state.list.map((object, index) => { return this.state.list[index] } ) }
            </div>
        </div>);
    }
}

I assume you're doing it this way because its an example but you could just do

this.state.list.map(listItem => listItem);

The code inside brackets still works simply because while JavaScript calculates the statement, but it doesn't return it.

In this particular case, the issue is with the arrow function itself, though. There is a specific rule arrows follow: when {} brackets are skipped, the statement is in fact treated as a return value, without having to use return keyword. This cleans the code a bit. But soon as you add the {} brackets, whatever is inside must be explicitly returned using the return keyword. The following will fix your issue:

{ this.state.list.map((object, index) => { return(this.state.list[index]) } ) }

Remember, React is still really just JavaScript (often ES6+.) As far as returning iterators specifically in React, see this nested components tutorial that pinpoints the issue with a few correct return use-cases. Yours is simply executing the statement.

Hope this helps.

The reason the second approach doesn't work is a syntactice subtlty with arrow functions.

When you wrap an arrow function that contains a single statement with { and }, then that arrow function is effecitvly equivilant to returning undefined.

So for instance:

(object, index) => { this.state.list[index] }

is equvilant to:

function(object, index) { 

   this.state.list[index];

   // Notice that nothing is returned
}

Conversly, by excluding the { and }, as you are doing in the working version:

{ this.state.list.map((object, index) => this.state.list[index] ) }

that is effectivly equaivlant to doing:

{ this.state.list.map(function(object, index) {

   // The function is returning something
   return this.state.list[index]
}

This return behaviour that "comes for free" with the non { .. } arrow functions, is the reason the first version works, and the latter version fails. For more information, see this MDN article

Related