React - Sorting Table Columns Inside a Map() Method

Viewed 1163

I am trying to sort a table (ascending/descending) when my table header data is placed inside a .map() method (see line 73 of the codesandbox link in this post).

I can get the hand emoji to change onClick, but no sorting takes place. I think it may have something to do with passing an object to the map method, and not an individual string or numeric value as found in the codesandbox that I have based this functionality on. Here is the original sandbox that I am modelling after: https://codesandbox.io/embed/table-sorting-example-ur2z9?fontsize=14&hidenavigation=1&theme=dark

...and here is my codesandbox with my data structure that I need to sort: https://codesandbox.io/s/table-sorting-example-forked-dofhj?file=/src/App.js

What can I change in order to get each column to be sortable? Any help/advice would be greatly appreciated.

App.js

import React from "react";
import "./styles.css";
import {
  Button,
  Table,
  Thead,
  Tbody,
  Flex,
  Tooltip,
  Tr,
  Th,
  Td
} from "@chakra-ui/react";

//Table Headers
const TABLE_HEADERS = [
  { name: "ID Number" },
  { name: "User Type" },
  { name: "User Category" },
  { name: "User Interest" }
];

const useSortableData = (items, config = null) => {
  const [sortConfig, setSortConfig] = React.useState(config);

  const sortedItems = React.useMemo(() => {
    let sortableItems = [...items];
    if (sortConfig !== null) {
      sortableItems.sort((a, b) => {
        if (a[sortConfig.key] < b[sortConfig.key]) {
          return sortConfig.direction === "ascending" ? -1 : 1;
        }
        if (a[sortConfig.key] > b[sortConfig.key]) {
          return sortConfig.direction === "ascending" ? 1 : -1;
        }
        return 0;
      });
    }
    return sortableItems;
  }, [items, sortConfig]);

  const requestSort = (key) => {
    let direction = "ascending";
    if (
      sortConfig &&
      sortConfig.key === key &&
      sortConfig.direction === "ascending"
    ) {
      direction = "descending";
    }
    setSortConfig({ key, direction });
  };

  return { items: sortedItems, requestSort, sortConfig };
};

const ProductTable = (props) => {
  const { items, requestSort, sortConfig } = useSortableData(
    props.myUserErrorTypes
  );

  const getClassNamesFor = (name) => {
    if (!sortConfig) {
      return;
    }
    return sortConfig.key === name ? sortConfig.direction : undefined;
  };
  return (
    <Table>
      <caption>User Error Types</caption>
      <Thead>
        <Tr>
          {TABLE_HEADERS.map(({ name, description, isNumeric }) => (
            <Th key={name} isNumeric={isNumeric}>
              <Button
                type="button"
                onClick={() => requestSort(name)}
                className={getClassNamesFor(name)}
              >
                <Tooltip label={description} aria-label={description}>
                  {name}
                </Tooltip>
              </Button>
            </Th>
          ))}
        </Tr>
      </Thead>
      <Tbody>
        {items.map((error) => {
          const { userNumber, userType, errorId, errorCategory } = error;
          return (
            <React.Fragment key={errorId}>
              <Tr id={errorId} key={errorId}>
                <Td>{userNumber}</Td>
                <Td>{userType}</Td>
                <Td>{errorId}</Td>
                <Td>{errorCategory}</Td>
                <Td textAlign="center">
                  <Flex justify="justifyContent"></Flex>
                </Td>
              </Tr>
            </React.Fragment>
          );
        })}
      </Tbody>
    </Table>
  );
};

export default function App() {
  return (
    <div className="App">
      <ProductTable
        myUserErrorTypes={[
          {
            userNumber: 1234567890,
            userType: "SuperUser",
            errorId: 406,
            errorCategory: "In-Progress"
          },
          {
            userNumber: 4859687937,
            userType: "NewUser",
            errorId: 333,
            errorCategory: "Complete"
          }
        ]}
      />
    </div>
  );
}

1 Answers

The items are being sorted by the table header name (e.g. 'ID Number') since you're calling requestSort with the name. Add an id property to the objects in the TABLE_HEADERS array matching the property name (e.g. userNumber) in the data objects and pass it as an argument to both the requestSort and getClassNamesFor functions.

const TABLE_HEADERS = [
  { name: 'ID Number', id: 'userNumber' },
  { name: 'User Type', id: 'userType' },
  { name: 'User Category', id: 'errorId' },
  { name: 'User Interest', id: 'errorCategory' },
]
{
  TABLE_HEADERS.map(({ name, id }) => (
    <Th key={id}>
      <Button
        type="button"
        onClick={() => requestSort(id)}
        className={getClassNamesFor(id)}
      >
        {name}
      </Button>
    </Th>
  ))
}

You're also trying to use the description and isNumeric values from the header object but they are both undefined. You might want to add those properties to the objects in the TABLE_HEADERS array.

Edit on CodeSandbox

Related