score:207

Accepted answer

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>
  }
}

score:-2

As of React 17, you can simply:

<div>
  {[1, 2, 3].map((e, i) => <p key={i}>{e}</p>)}
</div>

And it will render as:

<div>
  <p>1</p>
  <p>2</p>
  <p>3</p>
</div>

score:-1

Simply add join() after map and put it inside tag:

<span>{this.props.data.map((item) => item).join(', ')}</span>

score:-1

{errors.username.map((e,k) => <span key={k}>{e} <br/></span>)}

easiest way i could find.

score:0

This worked for me:

    {data.map( ( item, i ) => {
                  return (
                      <span key={i}>{item.propery}</span>
                    )
                  } ).reduce( ( prev, curr ) => [ prev, ', ', curr ] )}

score:0

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>
  }
}

score:0

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']

score:0

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.

score:0

function YourComponent(props) {

  const criteria = [];

  if (something) {
    criteria.push(<strong>{ something }</strong>);
  }

  // join the jsx elements with `, `
  const elements = criteria.reduce((accu, elem) => {
    return accu === null ? [elem] : [...accu, ', ', elem]
  }, null);

  // render in a jsx friendly way
  return elements.map((el, index) => <React.Fragment key={ index }>{ el }</React.Fragment> );

}

score:0

With flatMap (supported in all mainstream modern browsers as of June 2022), you can do it in a single step, something like this:

const Demo = ({ data }) => <div>{data.flatMap(
    (t, i) => [...(i ? [', '] : []), <span key={i}>{t}</span>]
)}</div>

ReactDOM.render(<Demo data={['tom', 'jason', 'chris']} />, document.body)
body { font-family: sans-serif; }
span { color: firebrick; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

If index == 0, the callback returns [span]; else, it returns [text, span]. Because we used flatMap instead of map, the resulting 2d array of [[span], [text, span], [text, span]] is flattened to [span, text, span, text, span], which React can render.

score:1

Use nested array to keep "," outside.

  <div>
      {this.props.data.map((element, index) => index == this.props.data.length - 1 ? <span key={index}>{element}</span> : [<span key={index}>{element}</span>, ", "])}
  </div>

Optimize it by saving the data to array and modify last element instead of checking if its last element all the time.

  let processedData = this.props.data.map((element, index) => [<span key={index}>{element}</span>, ", "])
  processedData [this.props.data.length - 1].pop()
  <div>
      {processedData}
  </div>

score:3

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, [])

score:6

My variant:

{this.props.data
    .map(item => <span>{item}</span>)
    .map((item, index) => [index > 0 && ', ', item ])}

score:9

the <span>{t}</span> you are returning is an object, not a string. Check the react docs about it https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

By using .join() on the returned array from map, you are joining the array of objects. [object Object], ...

You can put the comma inside the <span></span> so it gets rendered the way you want it to.

  render() {
    return (
      <div>
        { this.props.data.map(
            (t,i) => <span>{t}{ this.props.data.length - 1 === i ? '' : ','} </span>
          )
        }
      </div>
    )
  }

sample: https://jsbin.com/xomopahalo/edit?html,js,output

score:11

The accepted answer actually returns an array of arrays because prev will be an array each time. React is smart enough to make this work, but is it is prone to causing problems in the future such as breaking Reacts diffing algorithm when giving keys to each result of map.

The new React.Fragment feature lets us do this in an easy to understand manner without the underlying issues.

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map((t, index) => 
            <React.Fragment key={index}>
              <span> {t}</span> ,
            </React.Fragment>
          )
      </div>
  }
}

With React.Fragment we can simply place the separator , outside of the of the returned HTML and React won't complain.

score:19

If I just want to render a comma-separated array of components, I usually find reduce too verbose. A shorter solution in such cases is

{arr.map((item, index) => (
  <Fragment key={item.id}>
    {index > 0 && ', '}
    <Item {...item} />
  </Fragment>
))}

{index > 0 && ', '} will render a comma followed by a space in front of all array items except the first one.

If you want to separate the second-to-last item and the last one by something else, say the string ' and ', you can replace {index > 0 && ', '} with

{index > 0 && index !== arr.length - 1 && ', '}
{index === arr.length - 1 && ' and '}

score:22

Update with React 16: It's now possible to render strings directly, so you can simplify your code by removing all the useless <span> tags.

const list = ({ data }) => data.reduce((prev, curr) => [ prev, ', ', curr ]);

score:32

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()


Related Query

More Query from same tag