How to loop and render elements in React.js without an array of objects to map?

JavascriptJqueryReactjsReact Jsx

Javascript Problem Overview


I'm trying to convert a jQuery component to React.js and one of the things I'm having difficulty with is rendering n number of elements based on a for loop.

I understand this is not possible, or recommended and that where an array exists in the model it makes complete sense to use map. That's fine, but what about when you do not have an array? Instead you have numeric value which equates to a given number of elements to render, then what should you do?

Here's my example, I want to prefix a element with an arbitrary number of span tags based on it's hierarchical level. So at level 3, I want 3 span tags before the text element.

In javascript:

for (var i = 0; i < level; i++) {
    $el.append('<span class="indent"></span>');
}
$el.append('Some text value');

I can't seem to get this, or anything similar to work in a JSX React.js component. Instead I had to do the following, first building a temp array to the correct length and then looping the array.

React.js

render: function() {
  var tmp = [];
  for (var i = 0; i < this.props.level; i++) {
    tmp.push(i);
  }
  var indents = tmp.map(function (i) {
    return (
      <span className='indent'></span>
    );
  });

  return (
    ...
    {indents}
    "Some text value"
    ...
  );
}

Surely this can't be the best, or only way to achieve this? What am I missing?

Javascript Solutions


Solution 1 - Javascript

Updated: As of React > 0.16

Render method does not necessarily have to return a single element. An array can also be returned.

var indents = [];
for (var i = 0; i < this.props.level; i++) {
  indents.push(<span className='indent' key={i}></span>);
}
return indents;

OR

return this.props.level.map((item, index) => (
    <span className="indent" key={index}>
        {index}
    </span>
));

Docs here explaining about JSX children


OLD:

You can use one loop instead

var indents = [];
for (var i = 0; i < this.props.level; i++) {
  indents.push(<span className='indent' key={i}></span>);
}
return (
   <div>
    {indents}
    "Some text value"
   </div>
);

You can also use .map and fancy es6

return (
   <div>
    {this.props.level.map((item, index) => (
       <span className='indent' key={index} />
    ))}
    "Some text value"
   </div>
);
 

Also, you have to wrap the return value in a container. I used div in the above example

As the docs say here

>Currently, in a component's render, you can only return one node; if you have, say, a list of divs to return, you must wrap your components within a div, span or any other component.

Solution 2 - Javascript

Here is more functional example with some ES6 features:

'use strict';

const React = require('react');

function renderArticles(articles) {
    if (articles.length > 0) {      
        return articles.map((article, index) => (
            <Article key={index} article={article} />
        ));
    }
    else return [];
}

const Article = ({article}) => {
    return ( 
        <article key={article.id}>
            <a href={article.link}>{article.title}</a>
            <p>{article.description}</p>
        </article>
    );
};

const Articles = React.createClass({
    render() {
        const articles = renderArticles(this.props.articles);

        return (
            <section>
                { articles }
            </section>
        );
    }
});

module.exports = Articles;

Solution 3 - Javascript

Array.from() takes an iterable object to convert to an array and an optional map function. You could create an object with a .length property as follows:

return Array.from({length: this.props.level}, (item, index) => 
  <span className="indent" key={index}></span>
);

Solution 4 - Javascript

I'm using Object.keys(chars).map(...) to loop in render

// chars = {a:true, b:false, ..., z:false}

render() {
    return (
       <div>
        {chars && Object.keys(chars).map(function(char, idx) {
            return <span key={idx}>{char}</span>;
        }.bind(this))}
        "Some text value"
       </div>
    );
}

Solution 5 - Javascript

You can still use map if you can afford to create a makeshift array:

{
    new Array(this.props.level).fill(0).map((_, index) => (
        <span className='indent' key={index}></span>
    ))
}

This works because new Array(n).fill(x) creates an array of size n filled with x, which can then aid map.

array-fill

Solution 6 - Javascript

I think this is the easiest way to loop in react js

<ul>
    {yourarray.map((item)=><li>{item}</li>)}
</ul>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJon MilesView Question on Stackoverflow
Solution 1 - JavascriptDhirajView Answer on Stackoverflow
Solution 2 - JavascriptDmytro MedvidView Answer on Stackoverflow
Solution 3 - JavascriptconradjView Answer on Stackoverflow
Solution 4 - JavascriptMathdoyView Answer on Stackoverflow
Solution 5 - JavascriptSNagView Answer on Stackoverflow
Solution 6 - JavascriptNasar uddinView Answer on Stackoverflow