Search code examples
javascriptreactjscolumnsorting

How to make columns in table sortable in both ways using ReactJS


I am building a simple app in ReactJS that works with a JSON array by calling a certain API. I am then populating the results of the array in a table. I now wanted to make the columns of the table sortable. What I ideally want is to have both ascending and descending sorting. Once I click on the header when it is sorted ascending, it should sort descending and vice-versa. Here is my code.

class ParentComponent extends Component {
constructor(props) {
 super(props);
 this.state = { data: [] };
}

componentDidMount() {
  fetch("http://hostname:xxxx/yyyy/zzzz")
  .then(function(response) {
    return response.json();
  })
  .then(items => this.setState({ data: items }));
 }

render() {
var newdata = this.state.data;

return (
  <table className="m-table">
    <thead>
      <tr>
        <th>AccountName</th>
        <th>ContractValue</th>
      </tr>
    </thead>
    <tbody>
      {newdata.map(function(account, index) {
        return (
          <tr key={index} data-item={account}>
            <td data-title="Account">{account.accountname}</td>
            <td data-title="Value">{account.negotiatedcontractvalue}</td>
          </tr>
        );
      })}
    </tbody>
  </table>
  );
 }
}

export default ParentComponent;

Solution

  • You can add a sort property to your state with column and direction properties:

    state = {
      data: [],
      sort: {
        column: null,
        direction: 'desc',
      },
    };
    

    Of course you should also have a sort handler like so:

    onSort = (column) => (e) => {
      const direction = this.state.sort.column ? (this.state.sort.direction === 'asc' ? 'desc' : 'asc') : 'desc';
      const sortedData = this.state.data.sort((a, b) => {
        if (column === 'accountName') {
          const nameA = a.accountName.toUpperCase(); // ignore upper and lowercase
          const nameB = b.accountName.toUpperCase(); // ignore upper and lowercase
          if (nameA < nameB) {
            return -1;
          }
          if (nameA > nameB) {
            return 1;
          }
    
          // names must be equal
          return 0;
        } else {
          return a.contractValue - b.contractValue;
        }
      });
    
      if (direction === 'desc') {
        sortedData.reverse();
      }
    
      this.setState({
        data: sortedData,
        sort: {
          column,
          direction,
        }
      });
    };
    

    I got the sorting from MDN.

    Here's a sample pen: https://codepen.io/anon/pen/xrGJKv