Enter Your Email Address to Watch This Lesson

Your link to unlock this lesson will be sent to this email address.

Unlock this lesson and all 986 of the free egghead.io lessons, plus get React content delivered directly to your inbox!



Existing egghead members will not see this. Sign in.

Use map to Create React Components from Arrays of Data

4:18 React lesson by

React components can be dynamically generated based on a dataset. This lesson will show you how to do just that by mapping over the state.data object.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

React components can be dynamically generated based on a dataset. This lesson will show you how to do just that.

Avatar
Jeff

Why did you use the index of the array, rather than the id field of the data? If that array of users had been filterable, would that have been a problem?

In reply to egghead.io
Avatar
Joseph

In this instance using the array index as the key was simply the most efficient method to ensure a unique value. If we had been filtering the data in this example I would have used a simple array.filter before mapping the data to the PersonRow component... so using the array index would have worked just fine again.

In reply to Jeff
Avatar
Kirk

Why did you use map instead of forEach? I mostly use map when I need to return a transformed array. Was there some transforming happening here that I am not aware of?

In reply to egghead.io
Avatar
Joseph

Kirk, perhaps I am misunderstanding your question, but map does exactly what we need it to do here; return an array of PersonRow objects. It would take more code to do the same with a forEach.

You might want to take a look at the lesson on JSX ( https://egghead.io/lessons/jsx-deep-dive ). Just keep in mind that each tag in our JSX is a function and in this case the array of PersonRows is an argument passed to the <table> function as children.

The resulting js looks like this:
React.DOM.table(null,
this.state.data.map(function(person,i){
return PersonRow({key: i, data: person})
})
)

In reply to Kirk
Avatar
Kirk

Looks totally obvious now. Thanks.

In reply to Joseph
Avatar
Nick

This video could have been better. The purpose of the "key" is to track exactly which rows are coming and going from the list when the list changes. You should have put the real ID in, instead of just passing along the useless array index to placate the warning message.

It may appear to work when passing the array index in, but as soon as you try to add animations or put any more complex state into the rows, you may run into problems.

Even if you don't do that, if items are added or removed from the middle of the array, the DOM operations react generates will probably be inefficient. Instead of keeping the people mapped to their rows, things would shift and you'd have whole rows changed into different people, instead of simply adding/removing the effected DOM nodes.

In reply to Joseph
Avatar
Joseph

Nick, thanks for the feedback. While in this example passing the array index works perfectly, if this example was concerned with data that could change, or any of the scenarios you mentioned, I would use a key that would not change when that change in the data occurred. Thanks again!

In reply to Nick
Avatar
Huy

The video is so good. But can you please tell me when and what benefits to use stateless Component.
Is this the same with this code ? ( I bet it is because I tried it )

class Person extends React.Component{
render(){
return (

{this.props.data.id}
{this.props.data.name}

);
}
}

Thank you !

In this lesson, we're going to talk about iterating over a data set in order to create our JSX. Right here in our render method, right before our return statement, I'm going to say let items = this.state.items. That's going to set that data up.

We're going to set up our constructor, where we'll call super() to get our context. We're going to say this.state = {items: []}, and we'll set that to an array. Now to get that data, we're going to use fetch to make an AJAX call to the Star Wars API. We'll just hit up the people endpoint there.

App.js

import React from 'react';

class App extends React.Component {
  constructor() {
    super();
    this.state = {items: []}
  }
  componentWillMount(){
    fetch('http://swapi.co/api/people/?format=json')
      .then( response => response.json() )
      .then( data => {})
  }
  render(){
    let items = this.state.items
    return (
      <div>

      </div>
    )
  }
}

export default App

If you're not familiar with that, it's just this open source API that returns a bunch of data about Star Wars. When we get that data back, we're going to get results off of that. We're going to call it items, and then, we'll just set our state of items to that value.

componentWillMount(){
    fetch('http://swapi.co/api/people/?format=json')
      .then( response => response.json() )
      .then( ({results: items}) => this.setState({items}))
  }

Now that we've got that here in a render method, we can simply interpolate items.map. We can just map over those guys -- it's an array -- which will give us our item. We can just return something simple, like an <h4>.

render(){
  let items = this.state.items
  return (
    <div>
      {items.map(item => <h4>{item.name}</h4>)}
    </div>
  )
}

Inside that guy, say item.name, which is a value that we know we're going to get off of that. Save that, and we can see we've got our names coming in, but we've also got this warning down here that says each child in an array or iterator should have a unique key prop.

Iterator Warning

All that's saying is right here in our <h4>, we need a key prop that is equal to something unique. Now, we could use the index off of the iterator here. However, that's not going to be as performant as something completely unique to the record. Since, in this case, I don't have an ID, I'm going to use item.name. I'm going to save that.

<div>
      {items.map(item => 
        <h4 key={item.name}>{item.name}</h4>)}
    </div>

We get our data, and our warning is gone. Now we could have just as easily created a separate component for this. Let's say we've got one here called Person. It takes in its props. It's going to return the same <h4> with props.person.name.

const Person = (props) => <h4>{props.person.name}</h4>

Come up here, and rather than returning the <h4>, we're going to return a Person component. We'll say Person={item}. Save that, and now again, we've got our data, but we're back to the same error. Now in this case, it's not telling us that the key is needed on the <h4>, because in the context of this component, the <h4> has no siblings.

The key is needed amongst siblings. Here on the Person component, we're going to say key={item.name}. Save that, and everything is working fine.

<div>
  {items.map(item => 
    <Person key={item.name} person={item} />)}
</div>

If we wanted to get a look at how we can further use this JSX generation from a data set, let's say if this.state.filter -- we'll need to create that -- but if we have a filter, we'll say items = items.filter.

if(this.state.filter){
  items = items.filter( item =>
  item.name.toLowerCase()
    .includes(this.state.filter.toLowerCase()))
}

That'll give us our item. Then, we'll say if the item.name.toLowerCase includes this.state.filter, also toLowerCase, then, we'll have our filtered item. Let's go ahead and set up a quick filter method. This is just going to take an event off of an input. We're going to set our state of filter equal to e.target.value.

filter(e){
  this.setState({filter: e.target.value})
}

That'll just be the result of an <input> field. We'll just set that right here. Input, and on its onChange event, we'll say this.filter. We'll bind that to this. Save that. We've got our data. We've got our input field here. If I type L, we're going to get all the names with L. If I type C, we're going to get C3PO. V, we're going to get Darth Vader.

fin



HEY, QUICK QUESTION!
Joel's Head
Why are we asking?