thatgibbyguy
thatgibbyguy

Reputation: 4123

Rendering React Components from Array of Objects

I have some data called stations which is an array containing objects.

stations : [
  {call:'station one',frequency:'000'},
  {call:'station two',frequency:'001'}
]

I'd like to render a ui component for each array position. So far I can write

 var stationsArr = []
 for (var i = 0; i < this.data.stations.length; i++) {
     stationsArr.push(
         <div className="station">
             {this.data}
         </div>
     )
 }

And then render

render(){
 return (
   {stationsArr}
 )
}

The problem is I'm getting all of the data printing out. I instead want to just show a key like {this.data.call} but that prints nothing.

How can I loop through this data and return a new UI element for each position of the array?

Upvotes: 144

Views: 376713

Answers (6)

Unmitigated
Unmitigated

Reputation: 89482

Since React 16, it is completely valid to return an array of elements from a class component's render() method or a function component. See https://legacy.reactjs.org/blog/2017/09/26/react-v16.0.html#new-render-return-types-fragments-and-strings

There is no need for a wrapper element (like <div>) or a fragment. As such, the following code suffices for a function component:

const MyComponent = ({ stations }) => stations.map(station => 
           <div className="station" key={station.call}>{station.call}</div>);

However, it is important to keep in mind that every element in an array of elements that is rendered must have a unique key among the same array.

For example, React will warn about return [<div>Div 1</div>, <div>Div 2</div>] even though the array is static. It would have to be changed to add keys, e.g. return [<div key="div1">Div 1</div>, <div key="div2">Div 2</div>].

For a static number of values that need to be grouped together, fragments (introduced in React 16.2.0) are a better choice as they do not require keys on each element. This means that the following does not produce a warning:

return <>
    <div>Div 1</div>
    <div>Div 2</div>
</>;

In the case of mapping over an array, as in the question, keys are necessary regardless so fragments provide no advantage. The array after mapping can be directly returned instead.

Upvotes: 2

Sebastien Lorber
Sebastien Lorber

Reputation: 92210

You can map the list of stations to ReactElements.

With React >= 16, it is possible to return multiple elements from the same component without needing an extra html element wrapper. Since 16.2, there is a new syntax <> to create fragments. If this does not work or is not supported by your IDE, you can use <React.Fragment> instead. Between 16.0 and 16.2, you can use a very simple polyfill for fragments.

Try the following

// Modern syntax >= React 16.2.0
const Test = ({stations}) => (
  <>
    {stations.map(station => (
      <div key={station.call} className='station'>{station.call}</div>
    ))}
  </>
); 

// Modern syntax < React 16.2.0
// You need to wrap in an extra element like div here

const Test = ({stations}) => (
  <div>
    {stations.map(station => (
      <div className="station" key={station.call}>{station.call}</div>
    ))}
  </div>
); 

// old syntax
var Test = React.createClass({
    render: function() {
        var stationComponents = this.props.stations.map(function(station) {
            return <div className="station" key={station.call}>{station.call}</div>;
        });
        return <div>{stationComponents}</div>;
    }
});
 
var stations = [
  {call:'station one',frequency:'000'},
  {call:'station two',frequency:'001'}
]; 

ReactDOM.render(
  <div>
    <Test stations={stations} />
  </div>,
  document.getElementById('container')
);

Don't forget the key attribute!

https://jsfiddle.net/69z2wepo/14377/

Upvotes: 197

ZachHappel
ZachHappel

Reputation: 381

This is quite likely the simplest way to achieve what you are looking for.

In order to use this map function in this instance, we will have to pass a currentValue (always-required) parameter, as well an index (optional) parameter. In the below example, station is our currentValue, and x is our index.

station represents the current value of the object within the array as it is iterated over. x automatically increments; increasing by one each time a new object is mapped.

render () {
    return (
        <div>
            {stations.map((station, x) => (
                <div key={x}> {station} </div>
            ))}
        </div>
    );
}

What Thomas Valadez had answered, while it had provided the best/simplest method to render a component from an array of objects, it had failed to properly address the way in which you would assign a key during this process.

Upvotes: 3

Thomas Valadez
Thomas Valadez

Reputation: 1757

I have an answer that might be a bit less confusing for newbies like myself. You can just use map within the components render method.

render () {
   return (
       <div>
           {stations.map(station => <div key={station}> {station} </div>)} 
       </div>
   );
}

Upvotes: 61

Mo.
Mo.

Reputation: 27503

There are couple of way which can be used.

const stations = [
  {call:'station one',frequency:'000'},
  {call:'station two',frequency:'001'}
];
const callList = stations.map(({call}) => call)

Solution 1

<p>{callList.join(', ')}</p>

Solution 2

<ol>    
  { callList && callList.map(item => <li>{item}</li>) }
</ol>

Edit kind-antonelli-z8372

Of course there are other ways also available.

Upvotes: 2

nanobar
nanobar

Reputation: 66445

this.data presumably contains all the data, so you would need to do something like this:

var stations = [];
var stationData = this.data.stations;

for (var i = 0; i < stationData.length; i++) {
    stations.push(
        <div key={stationData[i].call} className="station">
            Call: {stationData[i].call}, Freq: {stationData[i].frequency}
        </div>
    )
}

render() {
  return (
    <div className="stations">{stations}</div>
  )
}

Or you can use map and arrow functions if you're using ES6:

const stations = this.data.stations.map(station =>
    <div key={station.call} className="station">
      Call: {station.call}, Freq: {station.frequency}
    </div>
);

Upvotes: 6

Related Questions