Search code examples
javascripttwitter-bootstrapreactjsbootstrap-4reactstrap

Using .map() helper with bootstrap <Col sm="4" /> layout in React


I'm using also ReduxLazyScroll for infinite scroll effect and it worked but before it was just ul -> li list. Now I want my app to generate 3 cards one beside another in bootstrap-grid style, like so:

    <Row>
      <Col sm="4">.col-3</Col>
      <Col sm="4">.col-3</Col>
      <Col sm="4">.col-3</Col>
    </Row>

And I have some logic problem, which I can't wrap my head around. So there is the code of parent component ( BeerListingScroll ) :

import React, { Component } from 'react';
import { Container, Row, Col } from 'reactstrap';
import ReduxLazyScroll from 'redux-lazy-scroll';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchBeers } from '../../actions/';

import BeersListItem from '../../components/BeersListItem';
import ProgressIndicator from '../../components/ProgressIndicator';

class BeerListingScroll extends Component {
  constructor(props) {
    super(props);

    this.loadBeers = this.loadBeers.bind(this);
  }

  loadBeers() {
    const { skip, limit } = this.props.beers;
    this.props.fetchBeers(skip, limit);
  }

  render() {
    const { beersArray, isFetching, errorMessage, hasMore } = this.props.beers;
    return (
      <Container>
        <ReduxLazyScroll
          isFetching={isFetching}
          errorMessage={errorMessage}
          loadMore={this.loadBeers}
          hasMore={hasMore}
        >
          {beersArray.map(beer => (
            <Row>
              <Col xs="12" sm="4">
                <BeersListItem key={beer.id} beer={beer} />
              </Col>
              <Col xs="12" sm="4">
                <BeersListItem key={beer.id} beer={beer} />
              </Col>
              <Col xs="12" sm="4">
                <BeersListItem key={beer.id} beer={beer} />
              </Col>
            </Row>
          ))}
        </ReduxLazyScroll>
      </Container>

    );
  }
}

function mapStateToProps(state) {
  return {
    beers: state.beers,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ fetchBeers }, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(BeerListingScroll);

So I'm using beersArray.map helper and I know that that logic doesn't make sense inside map:

       <Row>
          <Col xs="12" sm="4">
            <BeersListItem key={beer.id} beer={beer} />
          </Col>
          <Col xs="12" sm="4">
            <BeersListItem key={beer.id} beer={beer} />
          </Col>
          <Col xs="12" sm="4">
            <BeersListItem key={beer.id} beer={beer} />
          </Col>
        </Row>

because I'm loading each beer element 3 times and then it looks like that -> img enter image description here


The question is: How should I refactor that code to get first, second and third array element in the first row, 4th, 5th, 6th in the second row etc?


Desired outcome:

enter image description here

And here you have child component ( BeerListItem )

import React from 'react';
import {
  Card,
  CardImg,
  CardBody,
  CardTitle,
  CardSubtitle,
  Button,
} from 'reactstrap';

const BeerListItem = ({ beer }) => (
  <div>
    <Card>
      <CardImg top width="100%" src={beer.image_url} alt="beer" />
      <CardBody>
        <CardTitle>{beer.name}</CardTitle>
        <CardSubtitle>{beer.tagline}</CardSubtitle>
        <Button>More details</Button>
      </CardBody>
    </Card>
  </div>
);

export default BeerListItem;

And full project on github - > Link to github


Solution

  • If you want to do this you could group them together in 3's in a nested array, so instead of [beer1,beer2,beer3,beer4,...] you have [[beer1,beer2,beer3],[beer4,..,..],...] then you can iterate them in groups, with a row per beer group and a column per beer in that group:

    beerGroupsArray.map(beerGroup => (
        <Row>
            {
                beerGroup.map(beer => {
                    <Col xs="12" sm="4">
                        <BeersListItem key={beer.id} beer={beer} />
                    </Col>
                })
            }
        </Row>
    ))}
    

    That's just one example of how to do it though, based on your data and use case there might be a cleaner, more robust method.