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