In this article we will see how to iterate through a collection to render a list of elements in React. We will also have a look at how to fire callback from the children elements.

The Example

We will build a dynamic list of buttons with an ‘Add’ button to add a button to the list. A click on any of these buttons will log the position of the button in the list. Demo Here .

Rendering the List of Buttons

To create a list of elements using a for loop in React, create an empty array and push React elements directly into it. Then Render it like any other element:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
render: function() {

var buttonListElements = [], // Empty Array
n = 5,
i;

for(i = 0; i < n; i = i + 1) {
// For each element, push a React element into the array
buttonListElements.push(
<li key={i}><button>{i}</button></li>
);
}

// Then render the array using curly braces
return (
<div>
<ul>
{buttonListElements}
</ul>
</div>

)
}

Although it won’t be of any use in our app, React requires you to render a key attribute with unique values. If you don’t do so, you will receive warnings in the JS console.

Adding an onclick callback for each button

Let’s start with the function that will log the button index:

1
2
3
onChildButtonClick: function(val) {
console.log(val);
}

Then add the callback to each child button element by modifying the render function above:

1
2
3
4
5
for(i = 0; i < this.state.buttonNumber; i = i + 1) {
buttonListElements.push(
<li key={i}><button onClick={this.onChildButtonClick.bind(null, i)}>{i}</button></li>
);
}

To pass our current i value to each callback, use .bind(null, i). The function.prototype.bind() function will create a new function with a global context (null used as context) and the current value of i as the first argument (our val parameter in the onChildButtonClick function).

Dynamically add elements to the list

Let’s start by abstracting the number of elements value in the state:

1
2
3
getInitialState: function() {
return {buttonNumber: 5};
},

Follows the ‘Add’ button click callback:

1
2
3
4
onButtonClick: function() {
var newButtonNumber = this.state.buttonNumber + 1;
this.setState({buttonNumber: newButtonNumber});
},

Add the button element to our render method

1
2
3
4
5
6
<div>
<ul>
{buttonListElements}
</ul>
<button onClick={this.onButtonClick}>Add</button>
</div>

And replace the n with this.state.buttonNumber in the for loop:

1
for(i = 0; i < this.state.buttonNumber; i = i + 1)

This is it. Check out the demo here: https://jsbin.com/rakuya/edit?html,output

A cleaner way of handling loops

The example above is for explanation purposes. In production code, I would recommend the use of Array.prototype.map().

Like so:

1
2
3
4
5
6
7
8
var buttonListElements = [],
data = [{index: 0, text: 'Jane'}, {index: 1, text: 'John'}, {index: 2, text: 'Marcy'},];

data.map(function(item) {
buttonListElements.push(
<li key={item.index}><button onClick={this.onChildButtonClick.bind(null, item.index)}>{item.text}</button></li>
);
}, this);
  • With this method, you can pass an array of objects and elegantly access the children properties.
  • Do not forget to pass the context (this) as a second argument in arr.map(callback[, thisArg]). Otherwise you won’t be able to access the child click callback function (this.onChildButtonClick).

Thanks for reading !