javascript – React table with sorting and pagination doesn’t update

I am making a simple table in react where I have pagination (separate component) and sorting.

The table should sort update when table header is clicked. It changes state of data. The data is sorted but it doesn’t update. The table is rerendered when I change page (pagination component) and it does have sorted data. The problem is with

 this.state.pageOfItems.map((element, index) =>

if I change it to

 this.state.data.map((element, index) =>

then sorting works, but pagination doesn’t

Please help me get the table data updated immidiately when table header is clicked.

Table component:

    import React, { Component } from 'react';

    import classes from './Table.css';
    import data from 'data.json';
    import Pagination from '../Pagination/Pagination';

    class Table extends Component {
    constructor(props){
      super(props);
      this.state = {
        data: props.data,
        headings: Object.keys(props.data[0]),
        sortOrder: props.sortOrder || "original",
        sortKey: props.sortKey || null,
        pageOfItems: []
      };
      this.sortHandler = this.sortHandler.bind(this);
      this.onChangePage = this.onChangePage.bind(this);

    }

    componentWillMount() {
      //TODO: only apply this to the root element of the table component
      document.addEventListener('dragover', (event) => {
        event.preventDefault();
      });
    }

    sortHandler(e){
      const sortKey = e.target.dataset.sortcolumn;
      const currentSortKey = this.state.sortKey;
      const currentSortOrder = (sortKey === currentSortKey) ? this.state.sortOrder : "original";
      this.setState({sortKey: e.target.dataset.sortcolumn});
      console.log(currentSortOrder)
      switch(currentSortOrder){
        case "original":
        this.setState({sortOrder: "ascending"});
        this.state.data.sort((a, b)=>{
          if (a[sortKey] < b[sortKey]) {
            return -1;
          }
          if (a[sortKey] > b[sortKey]) {
            return 1;
          }
          return 0;
        });
        break;
        case "ascending":
        this.setState({sortOrder: "descending"});
        this.state.data.sort((a, b)=>{
          if (a[sortKey] < b[sortKey]) {
            return 1;
          }
          if (a[sortKey] > b[sortKey]) {
            return -1;
          }
          return 0;
        });
        break;
        case "descending":
        this.setState({sortOrder: "original"});
        this.state.data = this.props.data;
        break;
      }
      this.setState({data: this.state.data});
      console.log(this.state.data);
    }

    onChangePage(pageOfItems) {
      // update state with new page of items
      this.setState({ pageOfItems: pageOfItems });
    }

    render() {
      return (
        <div role="region" aria-labelledby={this.props.id} tabindex="0" style={{overflow: 'auto'}}>
        <table cellSpacing="0" cellPadding="0" data-sortorder={this.state.sortOrder}>
        {this.props.caption &&
          <caption id={this.props.id}>{this.props.caption}</caption>
        }
        <thead>
        <tr>
        {this.state.headings.map((element, index) => {
          return (
            <th
            data-sortcolumn={element}
            id={'header' + index}
            onClick={this.sortHandler}>
            {element}
            </th>
          )
        })}
        </tr>
        </thead>
        <tbody>
        {this.state.pageOfItems.map((element, index) => {
          return (
            <tr
            id={'row' + index}>
            {element && Object.values(element).map((cell)=>{
              return <td>
              {cell}
              </td>
            })}
            </tr>
          )
        })}
        </tbody>
        </table>
        <Pagination items={this.state.data} onChangePage={this.onChangePage} />
        </div>
      );
    }
  }

Pagination component:

import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
    items: PropTypes.array.isRequired,
    onChangePage: PropTypes.func.isRequired,
    initialPage: PropTypes.number
}

const defaultProps = {
    initialPage: 1
}

class Pagination extends React.Component {
constructor(props) {
    super(props);
    this.state = { pager: {} };
}

componentWillMount() {
    this.setPage(this.props.initialPage);
}

setPage(page) {
    var items = this.props.items;
    var pager = this.state.pager;

    if (page < 1 || page > pager.totalPages) {
        return;
    }

    // get new pager object for specified page
    pager = this.getPager(items.length, page);

    // get new page of items from items array
    var pageOfItems = items.slice(pager.startIndex, pager.endIndex + 1);

    // update state
    this.setState({ pager: pager });

    // call change page function in parent component
    this.props.onChangePage(pageOfItems);
}

getPager(totalItems, currentPage, pageSize) {
    // default to first page
    currentPage = currentPage || 1;

    // default page size is 10
    pageSize = pageSize || 5;

    // calculate total pages
    var totalPages = Math.ceil(totalItems / pageSize);

    var startPage, endPage;
    if (totalPages <= 10) {
        // less than 10 total pages so show all
        startPage = 1;
        endPage = totalPages;
    } else {
        // more than 10 total pages so calculate start and end pages
        if (currentPage <= 6) {
            startPage = 1;
            endPage = 10;
        } else if (currentPage + 4 >= totalPages) {
            startPage = totalPages - 9;
            endPage = totalPages;
        } else {
            startPage = currentPage - 5;
            endPage = currentPage + 4;
        }
    }

    // calculate start and end item indexes
    var startIndex = (currentPage - 1) * pageSize;
    var endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);

    // create an array of pages to ng-repeat in the pager control
    var pages = [...Array((endPage + 1) - startPage).keys()].map(i => startPage + i);

    // return object with all pager properties required by the view
    return {
        totalItems: totalItems,
        currentPage: currentPage,
        pageSize: pageSize,
        totalPages: totalPages,
        startPage: startPage,
        endPage: endPage,
        startIndex: startIndex,
        endIndex: endIndex,
        pages: pages
    };
}

render() {
    var pager = this.state.pager;

    return (
        <ul className="pagination">
            <li className={pager.currentPage === 1 ? 'disabled' : ''}>
                <a onClick={() => this.setPage(pager.currentPage - 1)}>&lt; back</a>
            </li>
            {pager.pages.map((page, index) =>
                <li key={index} className={pager.currentPage === page ? 'active' : ''}>
                    <a onClick={() => this.setPage(page)}>{page}</a>
                </li>
            )}
            <li className={pager.currentPage === pager.totalPages ? 'disabled' : ''}>
                <a onClick={() => this.setPage(pager.currentPage + 1)}>next &gt;</a>
            </li>
        </ul>
    );
}
}

Pagination.propTypes = propTypes;
Pagination.defaultProps
export default Pagination;

EDIT: A part of the pagination has been moved to a table component and now it works correctly

Leave a Comment