I have one component which is going to display Array of String. The code looks like this:
React.createClass({
render() {
this.props
Using es6 you could do something like:
const joinComponents = (accumulator, current) => [
...accumulator,
accumulator.length ? ', ' : '',
current
]
And then run:
listComponents
.map(item => <span key={item.id}> {item.t} </span>)
.reduce(joinComponents, [])
You can use reduce
to combine multiple elements of an array:
React.createClass({
render() {
<div>
this.props.data
.map(t => <span>t</span>)
.reduce((accu, elem) => {
return accu === null ? [elem] : [...accu, ',', elem]
}, null)
</div>
}
})
This initializes the accumulator with null, so we can wrap the first item in an array. For each following element in the array, we construct a new array that contains all previous elements using the ...-operator
, add the separator and then the next element.
Array.prototype.reduce()
As mentioned by Pith, React 16 allow you to use strings directly so wrapping the strings in span tags are no longer needed. Building on Maarten's answer, if you also want to deal with a custom message right away (and avoid throwing an error on empty array), you could lead the operation with a ternary if statement on the length property on the array. That could look something like this:
class List extends React.Component {
const { data } = this.props;
render() {
<div>
{data.length
? data.reduce((prev, curr) => [prev, ', ', curr])
: 'No data in the array'
}
</div>
}
}
Am I the only who thinks there's a lot of needless spreading and nesting going on in the answers here? Points for being concise, sure, but it leaves the door open to issues of scale or React changing how they deal with nested arrays/Fragments.
const joinJsxArray = (arr, joinWith) => {
if (!arr || arr.length < 2) { return arr; }
const out = [arr[0]];
for (let i = 1; i < arr.length; i += 1) {
out.push(joinWith, arr[i]);
}
return out;
};
// render()
<div>
{joinJsxArray(this.props.data.map(t => <span>t</span>), ', ')}
</div>
One array, no nesting. No sexy method chaining either, but if you find yourself doing this often you can always add it to the array prototype or wrap it in a function that takes a mapping callback as well to do it all in one go.
This should return a flat array. Handle s case with non iterable first object by providing an initial empty array and filters the not needed first comma from the outcome
[{}, 'b', 'c'].reduce((prev, curr) => [...prev, ', ', curr], []).splice(1) // => [{}, 'b', 'c']
A simple solution is to use reduce()
without second argument and without spreading the previous result:
class List extends React.Component {
render() {
<div>
{this.props.data
.map(t => <span>{t}</span>)
.reduce((prev, curr) => [prev, ', ', curr])}
</div>
}
}
Without second argument, reduce()
will start at index 1 instead of 0, and React is perfectly happy with nested arrays.
As said in the comments, you want to only use this for arrays with at least one item, because reduce()
without second argument will throw with an empty array. Normally this should not be a problem, since you want to display a custom message saying something like 'this is empty' for empty arrays anyway.
Update for Typescript
You can use this in Typescript (without type-unsafe any
) with a React.ReactNode
type parameter on .map()
:
class List extends React.Component {
render() {
<div>
{this.props.data
.map<React.ReactNode>(t => <span>{t}</span>)
.reduce((prev, curr) => [prev, ', ', curr])}
</div>
}
}