How to sort table by asc and desc all field in React js

9.3k Views Asked by At

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.but problem is its working only one time not working like both asc and desc order so how to do to this in both asc and desc. Here is my code.

import React, { Component } from "react";
import "./styles.css";

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

  async componentDidMount() {
    try {
      const res = await fetch(`https://digitalfleet.eu/api/1/drivers/`);
      const driver = await res.json();
      console.log(driver);
      this.setState({
        driver
      });
    } catch (e) {
      console.log(e);
    }
  }

  compareBy(key) {
    return function (a, b) {
      if (a[key] < b[key]) return -1;
      if (a[key] > b[key]) return 1;
      return 0;
    };
  }

  sortBy(key) {
    let arrayCopy = [...this.state.driver ];
    arrayCopy.sort(this.compareBy(key));
    this.setState({driver: arrayCopy});
  }

  render() {
    const { driver } = this.state;
    return (
      <div className="App">
        <table className="table table-hover">
          <thead>
            <tr className="th ">
              <th style={{ width: "5%" }}>id</th>
              <th>Name</th>
              <th className="">Car</th>
              <th className="" onClick={() => this.sortBy('milage')}>Milage </th>
              <th className="">Fuel Consumed</th>
              <th className="">Average Fuel</th>
              <th className="" onClick={() => this.sortBy('overspeeding_distance')}>Overspeeding Distance</th>
              <th onClick={() => this.sortBy('critical_overspeed')}>Critical Speed</th>
              <th onClick={() => this.sortBy('overallscore')}>Score</th>
            </tr>
          </thead>
          <tbody>
            {/* currPage.data */}
            {driver.map((c, i) => (
              <tr key={c.pk}>
                <td>{i + 1}</td>
                <td style={{ color: "#b71c1c" }} className="font-weight-bolder">
                  {c.name ? `${c.name}` : " ? "}
                  {/* </a> */}
                </td>
                <td>{c.carquanttity ? `${c.carquanttity}` : "-"} </td>
                <td>{c.milage ? `${c.milage.toFixed(1)}` : "-"} </td>
                <td>
                  {c.fuel_consumed ? `${c.fuel_consumed.toFixed(1)}` : "-"}
                </td>
                <td>
                  {c.average_fuel_consumed
                    ? `${c.average_fuel_consumed.toFixed(1)}`
                    : "-"}{" "}
                </td>
                <td>
                  {c.overspeeding_distance
                    ? `${c.overspeeding_distance.toFixed(1)}`
                    : "-"}
                </td>
                <td>
                  {c.critical_overspeed
                    ? `${c.critical_overspeed.toFixed(1)}`
                    : "-"}
                </td>
                <td>
                  {c.overallscore ? `${c.overallscore.toFixed(1)}` : " - "}
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }
}

export default App;

And here is codesanbox link of my code. https://codesandbox.io/s/pensive-mountain-i259j

2

There are 2 best solutions below

2
user2485861 On BEST ANSWER

You have written same sort functionality for both ascending and desc while it should return different type in both cases.

without maintaining additional flag I fixed it .Check this out Visit https://codesandbox.io/s/pensive-engelbart-i6n2y

0
ArielGro On

You need to add a direction to your compare function, and use it to reverse the results as follows:

compareBy(key, ascending) {
    let reverse = ascending ? 1 : -1;
    return function (a, b) {
      if (a[key] < b[key]) return -1 * reverse;
      if (a[key] > b[key]) return 1 * reverse;
      return 0;
    };
  }

Now, you will have to keep a boolean for each row you want to sort by, and toggle it (turn true to false and vice versa) on each click

<th className="" onClick={() => {this.milageAsc = !this.milageAsc; this.sortBy('milage', this.milageAsc)}}>Milage </th>

The flag can be also used for toggling a class to an arrow sign that indicates the sort order on the column's header...

I might got the Ascending / Descending direction wrong but I hope you catch my drift