195

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?

2

6 Answers 6

287

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.

Sign up to request clarification or add additional context in comments.

9 Comments

That works, and it's much simpler thanks. Yes I'm aware that you have to wrap return value in container, I'm doing that already it's just missing from the example.
add keys inside the loop. keys are important in react.
i've been looking for you all of my life
@ElgsQianChen Its not possible. It has to be wrapped with some tag. If {indents} returns a single dom elements with content inside it, then its ok
I don't understand why the map method is mentioned in this answer, as it does only work for Array objects, which the question clearly states is not the case.
|
51

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;

5 Comments

This looks like the most 'Reacty' way to do it. Pass values as props to another sub component. Thanks!
This is a great! Perfect for when your render() is html heavy.
To make it more ES6 you could use import React from "react" and export default Articles
This answer doesn't even attempt to answer the question. The question was clear, how to convert a for loop to a map'able array (or object) in order to render n number of items in a React component without having an array of items. You solution completely ignores that fact and assumes being passed an array of articles from props.
The question is very specific about NOT having an array of objects.
37

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>
);

1 Comment

Just what I needed for rendering X number of elements, thank you!
16

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>
    );
}

2 Comments

Your answer worked for me, but only after I added chars && ... and .bind(this) on the end of my function. I'm curious why merely Object...(so on and so forth) didn't work. I kept getting undefined.
This doesn't answer the question, it specifically says without an array of objects to parse and the explanation explicitly says that I want to convert a for loop to map for rendering in a React component. You substituted array for an object which doesn't help answer the question, or add any further value.
8

Update:

You can map in combination with Array.from :

{
    Array.from({ length: this.props.level }).map((_, index) => (
        <span className='indent' key={index}></span>
    ))
}

Original :

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

Comments

-1

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

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

1 Comment

This does not answer the question, please read the question in full before attempting to answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.