Reputation: 4123
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
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
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
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
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
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>
Of course there are other ways also available.
Upvotes: 2
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