React bootstrap table - Part 1

Sometime back I came across a library called React Bootstrap Table. This helps you to build the table with loads of features like Pagination, Filtering, Sorting, Export to CSV and many more. And even it allows you to customise as per your need. I’ve explored various grid / table component libraries to use, but many came with difficulty to either learning or implementing or lack of expected feature. So I’ve got settled with this library and moreover it is actively maintained.

What am I going to show?

  • Basic table with data fetched from remote with pagination - Part 1
  • Adding some filters - Part 2
  • Customising few fields based on our needs - Part 3

What not?

  • I’m not going to show you how to write react app with redux. I assume that you should know react and redux already. If not, kindly head to this tutorial written by Cory House. Because the sample app created follows exactly the same structure what he taught in that tutorial.

Demo

You can download the sample from here. This has the instructions on how to setup and run the demo.

Mock Data

Before writing our component, we need some data to populate our table. I used Mockaroo to generate the mock data. The mock data contains id, productName, price, manufacturedDate, expiryDate elements with 100 rows. I would like to show 10 rows in table with 10 paginated links. This way at any point of time, user’s browser will only have 10 rows of data. For every pagination navigation, there will be a fetch call triggered.

Mock Api

To ease certain things, I use mock api instead of real api, which will return the data when queried. Create a file called mockApi.js in src/api/mockApi.js.

import _ from 'underscore'

// Mock data generated from https://www.mockaroo.com/
const mockData = [
  {
    id: 1,
    productName: 'pain reliever',
    price: 14,
    manufacturedDate: '2016-08-01',
    expiryDate: '2016-07-11'
  },
  {
    id: 2,
    productName: 'Dorzolamide Hydrochloride and Timolol Maleate',
    price: 100,
    manufacturedDate: '2015-12-30',
    expiryDate: '2016-05-29'
  }
]

class ProductApi {
  // Get the products by page number
  static getProducts(page) {
    return new Promise((resolve, reject) => {
      // Just to simulate a delay
      setTimeout(() => {
        let products = {}
        products.pageCount = 10
        products.resultsCount = 100
        products.productList = _.first(_.rest(mockData, page * 10 - 10), 10) // Divides the 100 data by chunks of 10
        resolve(Object.assign({}, products))
      }, 100)
    })
  }
}

export default ProductApi

The getProducts method will be triggered by the redux action to fetch the data. Refer src/actions/productActions.js on how to trigger from action. We also need the data populated on the table once the page is landed. Refer src/index.js on how we dispatch an action. Using the redux reducer, we stored the data into the state called products. Let’s create our component.

Components

For this tutorial, I’m going to create 2 components:

  1. HomePage - A container component
  2. ProductList - A presentational component

Let’s build our presentational component first.

ProductList

Create ProductList.js in src/components/home/ProductList.js and paste the following code:

import React, { PropTypes } from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';

/**
A product list component which shows the table with list of data
**/
const ProductList = ({
  products,
  activePage,
  onNavigatePage
}) => {
  const productList = products.productList;

  const options = {
    hideSizePerPage: true,
    page: activePage,
    onPageChange: onNavigatePage
  };

  return(
    <bootstraptable data="{productList}" fetchinfo="{{dataTotalSize:" products.resultscount}}="" options="{options}" remote="" hover="" pagination=""><tableheadercolumn iskey="" datafield="id">Id</tableheadercolumn>
      <tableheadercolumn datafield="productName">Product</tableheadercolumn>
      <tableheadercolumn datafield="price">Price</tableheadercolumn>
      <tableheadercolumn datafield="manufacturedDate">Manufacture Date</tableheadercolumn>
      <tableheadercolumn datafield="expiryDate">Expiry Date</tableheadercolumn></bootstraptable>
  );
};

ProductList.propTypes = {
  products: PropTypes.object.isRequired,
  activePage: PropTypes.number.isRequired,
  onNavigatePage: PropTypes.func.isRequired
};

export default ProductList;

We start with importing BootstrapTable and TableHeaderColumn from react-bootstrap-table library. Then we initialize our ProductList presentational component with three properties:

  1. `products` - an object which we get from our mock api
  2. `activePage` - the current page number
  3. `onNavigatePage` - a function which will call our action (remember the action calls the mockApi) to update the data and updates the active page

In the return, we initiate the BootstrapTable component with the required parameters which will be passed to our container component to render. Few things to note here:

  1. `data` - takes the list of items to render in the table. In our case we are passing 10 items of product list
  2. `fetchInfo` - in which we specify how much data we have, based on that the number of pages will be calculated. In our case, we are saying 100 rows, so the library will divide 100 by 10 by default and show 10 pages in the pagination links.
  3. `options` - this is where we say what is our current page, what should happen if we navigate to another page and so on.
  4. `remote` - when it is true, the library ensures the data is coming from external source. In our case, it is coming from `mockApi`.

Next thing, for each field we create a TableHeaderColumn by saying my id column is the unique using isKey attribute.

Let’s create our container component.

HomePage

Create HomePage.js in src/components/home/HomePage.js and paste the following there:

import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as productActions from '../../actions/productActions'
import ProductList from './ProductList'

class HomePage extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.state = {
      activePage: 1
    }
    this.onNavigatePage = this.onNavigatePage.bind(this)
  }

  // This calls the loadAllProducts action and
  // also updates the activePage state to the navigated page number
  onNavigatePage(page, sizePerPage) {
    this.props.actions.loadAllProducts(page)
    this.setState({ activePage: page })
  }

  render() {
    const { products } = this.props
    return (
      Object.keys(products).length > 0 && (
        <ProductList
          products={products}
          activePage={this.state.activePage}
          onNavigatePage={this.onNavigatePage}
        />
      )
    )
  }
}

HomePage.propTypes = {
  products: PropTypes.object.isRequired,
  actions: PropTypes.object.isRequired
}

function mapStateToProps(state, ownProps) {
  return {
    products: state.products
  }
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(productActions, dispatch)
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomePage)

Direct jump to render method. The render method simply initiates a ProductList component with the required properties which we have explained earlier.

The onNavigatePage method is implemented here which takes care of calling the action for the required page and udpates the activePage state so that the pagination control set the current page as active. That’s it.

Now if you run npm start -s, and head to browser where it opened http://localhost:3005 you could see the table with pagination as like this:

React bootstrap table example

We’ll again meet soon with the Part-2.