Reputation: 44739
I've written some code to render repeating elements in ReactJS, but I hate how ugly it is.
render: function(){
var titles = this.props.titles.map(function(title) {
return <th>{title}</th>;
});
var rows = this.props.rows.map(function(row) {
var cells = [];
for (var i in row) {
cells.push(<td>{row[i]}</td>);
}
return <tr>{cells}</tr>;
});
return (
<table className="MyClassName">
<thead>
<tr>{titles}</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
Is there a better way to achieve this?
(I would like to embed for
loops within the template code, or some similar approach.)
Upvotes: 87
Views: 151442
Reputation: 130065
Since Array(3)
will create an un-iterable array, it must be populated to allow the usage of the map
Array method. A way to "convert"
is to destruct it inside Array-brackets, which "forces" the Array to be filled with undefined
values, same as Array(N).fill(undefined)
<table>
{ [...Array(3)].map((_, index) => <tr key={index}/>) }
</table>
fill()
:<table>
{ Array(3).fill(<tr/>) }
</table>
⚠️ Problem with above example is the lack of
key
prop, which is a must.
(Using an iterator'sindex
askey
is not recommended)
const tableSize = [3,4]
const Table = (
<table>
<tbody>
{ [...Array(tableSize[0])].map((tr, trIdx) =>
<tr key={trIdx}>
{ [...Array(tableSize[1])].map((a, tdIdx, arr) =>
<td key={trIdx + tdIdx}>
{arr.length * trIdx + tdIdx + 1}
</td>
)}
</tr>
)}
</tbody>
</table>
);
ReactDOM.render(Table, document.querySelector('main'))
td{ border:1px solid silver; padding:1em; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<main></main>
Upvotes: 21
Reputation: 44880
You can put expressions inside braces. Notice in the compiled JavaScript why a for
loop would never be possible inside JSX syntax; JSX amounts to function calls and sugared function arguments. Only expressions are allowed.
(Also: Remember to add key
attributes to components rendered inside loops.)
JSX + ES2015:
render() {
return (
<table className="MyClassName">
<thead>
<tr>
{this.props.titles.map(title =>
<th key={title}>{title}</th>
)}
</tr>
</thead>
<tbody>
{this.props.rows.map((row, i) =>
<tr key={i}>
{row.map((col, j) =>
<td key={j}>{col}</td>
)}
</tr>
)}
</tbody>
</table>
);
}
JavaScript:
render: function() {
return (
React.DOM.table({className: "MyClassName"},
React.DOM.thead(null,
React.DOM.tr(null,
this.props.titles.map(function(title) {
return React.DOM.th({key: title}, title);
})
)
),
React.DOM.tbody(null,
this.props.rows.map(function(row, i) {
return (
React.DOM.tr({key: i},
row.map(function(col, j) {
return React.DOM.td({key: j}, col);
})
)
);
})
)
)
);
}
Upvotes: 133
Reputation: 119
This is, imo, the most elegant way to do it (with ES6). Instantiate you empty array with 7 indexes and map in one line:
Array.apply(null, Array(7)).map((i)=>
<Somecomponent/>
)
kudos to https://php.quicoto.com/create-loop-inside-react-jsx/
Upvotes: 3
Reputation: 6073
To expand on Ross Allen's answer, here is a slightly cleaner variant using ES6 arrow syntax.
{this.props.titles.map(title =>
<th key={title}>{title}</th>
)}
It has the advantage that the JSX part is isolated (no return
or ;
), making it easier to put a loop around it.
Upvotes: 14
Reputation: 86220
In the spirit of functional programming, let's make our components a bit easier to work with by using abstractions.
// converts components into mappable functions
var mappable = function(component){
return function(x, i){
return component({key: i}, x);
}
}
// maps on 2-dimensional arrays
var map2d = function(m1, m2, xss){
return xss.map(function(xs, i, arr){
return m1(xs.map(m2), i, arr);
});
}
var td = mappable(React.DOM.td);
var tr = mappable(React.DOM.tr);
var th = mappable(React.DOM.th);
Now we can define our render like this:
render: function(){
return (
<table>
<thead>{this.props.titles.map(th)}</thead>
<tbody>{map2d(tr, td, this.props.rows)}</tbody>
</table>
);
}
An alternative to our map2d would be a curried map function, but people tend to shy away from currying.
Upvotes: 2