1

I am looping through a set of items in a reactjs component. I'm trying to wrap the items in a div every 3 items.

            {
              lang.carouselFeatures[0].items.map(function (item, index) {
                return (
                  {
                    index % 3 === 0
                      ? <div className='row'>
                      : null
                  }
                  <div key={index} className='medium-18 medium-offset-2 columns'>
                    <a rel='nofollow' href='#page-01-med'>
                      <div className='relative-container'>
                        <img className='icn__large icn--no-margin-bottom centered' src={Thumb1} />
                        <div className='icn--hover__features' />
                      </div>
                      <p className='text--center paragraph-margin-top-10'>Live Video Interviews</p>
                    </a>
                  </div>
                  {
                    index % 3 === 0
                      ? </div>
                      : null
                  }
                )
              })
            }

I am trying to recreate this static piece of markup, so also need to add to the wrapper an additional class if its not the first series. Answers/Suggestions welcome. enter image description here

///fixed

    {
     lang.carouselFeatures[0].items.reduce((m, k, i) => {
       if (i % 3 === 0) {
         m.push([k])
       } else {
         m[m.length - 1].push(k)
       }
       return m
     }, []).map((grouped, index) => (
       <div key={index} className='row'>
         {
           grouped.map((item, j) =>
             <div key={j} className='medium-18 medium-offset-2 columns'>
               <a rel='nofollow' href='#page-01-med'>
                 <div className='relative-container'>
                   <img className='icn__large icn--no-margin-bottom centered' src={item.image} />
                   <div className='icn--hover__features' />
                 </div>
                 <p className='text--center paragraph-margin-top-10'>{item.title}</p>
               </a>
             </div>
            )
         }
       </div>
      ))
    }
The Old County
  • 89
  • 13
  • 59
  • 129

2 Answers2

1

React expects you to return a React component. You cannot "split" a React component and return a closing tag where it fits.

What you do here reminds of the kind of logic you would do in languages such as php where you would just echo the html elements in text. In php that works because html is parsed at runtime and doesn't need compilation. This isn't the case with React.

Instead you need to return one component at a time, and to do that you need a slightly more complex algorithm. Here I use Array.reduce():

class MyApp extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: [1, 2, 3, 4, 5, 6],
      chunkSize: 3
    };
  }
  
  renderGroups() {
    let a = this.state.arr.reduce((acc, item, idx) => {
      let group = acc.pop();
      if (group.length == this.state.chunkSize) {
        acc.push(group);
        group = [];
      }
      group.push(item);
      acc.push(group);
      return acc;
    }, [[]]);
    
    return a.map((item) => {
      return (
        <div key={item} className='row'>
          {item.map(x => <div className='medium-18 medium-offset-2 columns'>{x}</div>)}
        </div>
      );
    });
  }
  
  render() {
    return (
      <div>
        {this.renderGroups()}
      </div>
    );
  }
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
.row {
  background: red;
  border: 1px solid black;
  margin: 2px;
}

.columns {
  display: inline-block;
  background: white;
  margin: 2px;
  padding: 0 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

For a detailed explanation of what happens inside the reduce(), have a look at an earlier answer of mine, here.

Community
  • 1
  • 1
Chris
  • 57,622
  • 19
  • 111
  • 137
1

Every Html like tag written in jsx is converted to plain javascript. In your code, Jsx element is broken as <div> is not a valid element. It should be either <div/> or <div></div>

You can group your data in map reduce chain like this:

{ 
 lang.carouselFeatures[0].items.reduce((m,k,i)=>{
  if(i%3==0){ 
     m.push([k]) 
  }else{ 
     m[m.length-1].push(k) 
  }
  return m;
 },[]).map(grouped=>(<div>{grouped}</div>))
}
Partha Pal
  • 300
  • 1
  • 5
  • I've tried to get to the grouped var but its busted in my above example – The Old County May 17 '17 at 13:37
  • I forgot to split the grouped array. You need to use a second map in grouped. Edited the code in fiddle. Please check this : https://jsfiddle.net/parthapal33/4z5r0kcx/1/ – Partha Pal May 19 '17 at 11:17