10

The documention for useSortBy sortType properties says:

sortType: String | Function(rowA: <Row>, rowB: <Row>, columnId: String, desc: Bool)

    Used to compare 2 rows of data and order them correctly.
    If a function is passed, it must be memoized. The sortType function should return -1 if rowA is larger, and 1 if rowB is larger. react-table will take care of the rest.
    String options: basic, datetime, alphanumeric. Defaults to alphanumeric.
    The resolved function from the this string/function will be used to sort the this column's data.
        If a string is passed, the function with that name located on either the custom sortTypes option or the built-in sorting types object will be used.
        If a function is passed, it will be used.
    For more information on sort types, see Sorting

but doesn't explain fully how to use it.

So how does one supply a sortType function?

Tom
  • 6,325
  • 4
  • 31
  • 55

5 Answers5

23

The arguments for the sortType function are: (rowA, rowB, columnId, desc)

columnId identifies which column the row is being sorted by, and so allows getting the cell value.

desc identifies the direction of the sort. Even though desc is supplied, the sort function should NOT reverse the return values. react table automatically does this.

For example:

sortType: React.useMemo((rowA, rowB, id, desc) => {
       if (rowA.values[id] > rowB.values[id]) return 1; 
       if (rowB.values[id] > rowA.values[id]) return -1;
        return 0;
})

example of where to use sortType:

const columns = [{       
        Header: ...
        accessor: ...
        sortType: /*sortType func goes here... */        
}, ...]

function MyTable(columns, data)
{
 const { /*...*/ } = useTable({columns,data})
}
Tom
  • 6,325
  • 4
  • 31
  • 55
  • Thanks for this, but I'm not sure how you could come to that answer from the react-table docs... How do you know that rows will have an property of type object that you can access with the id property? I am also getting the error "React Hook "React.useMemo" cannot be called in a class component" since I am constructing my columns in a class component, then passing them to the presentational component that builds my table. Is there a workaround for that? – rook218 Dec 19 '20 at 00:10
  • 1
    https://react-table.tanstack.com/docs/api/useSortBy You need to pass this function to Columns. I'm still working on how to do it myself but I'll post when I come up with something. You can't use hooks in a react class component. I'd put everything that is related to react-table in functional components. All the documentation has them in the same file but I'm also trying to get some separation. – Eman4real Feb 04 '21 at 17:55
  • @Tom, In your sort function above I suspect most people would use `rowA.values[id]` rather than `rowA.original[id]`. Also, the common semantics of `>` might return `1` rather than `-1`. – Ken Lin Aug 28 '21 at 15:12
  • @Eman4real, no need to inline your function. Simply supply `sortType` with the *name* of your compare function (e.g., `compareNumericString`) as I've done [here](https://stackoverflow.com/a/68965542/1990647). – Ken Lin Aug 28 '21 at 15:20
9

Per your doc citation, sortType is a Column option.

The following options are supported on any Column object passed to the columns options in useTable()

For instance, modify the Quick Start's Define Columns, like so:

const columns = React.useMemo(
  () => [
    {
      Header: 'Column 1',
      accessor: 'col1', // accessor is the "key" in the data
    },
    {
      Header: 'Column 2',
      accessor: 'col2',
      sortType: compareNumericString // custom function
    },
  ],
  []
)

function compareNumericString(rowA, rowB, id, desc) {
    let a = Number.parseFloat(rowA.values[id]);
    let b = Number.parseFloat(rowB.values[id]);
    if (Number.isNaN(a)) {  // Blanks and non-numeric strings to bottom
        a = desc ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
    }
    if (Number.isNaN(b)) {
        b = desc ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
    }
    if (a > b) return 1; 
    if (a < b) return -1;
    return 0;
}
Ken Lin
  • 1,819
  • 21
  • 21
5

I had quite a bit of trouble figuring this one out as well. Here's how I did it. It's in typescript, but if you need it in plain js, just remove all the typings. 1st, here' the custom sort. It will sort strings, and always put the nulls/blanks/undefined at the end.

const customStringSort: any = (rowA: Row, rowB: Row, columnId: string, desc: boolean) => {
  const defaultVal = desc ? 'AAAAAAAAAAAA' : 'ZZZZZZZZ';
  return (rowA.values[columnId] ?? defaultVal)
    .localeCompare(rowB.values[columnId] ?? defaultVal);
};

There are 2 things to notice about this.

  1. I couldn't figure out why typescript didn't like it when the return was defined as a number. I hate to use any, but this works.
  2. The react table documentation indicates that this must be memoized. This is not, but it works still.

Next you have to add this function to the sortTypes.

const sortTypes: Record<string, SortByFn<SomeObject>> = {
  customStringSort: customStringSort,
};

Next, add the sortTypes to the useTable instance.

const {
  getTableProps,
  getTableBodyProps
  headerGroups,
  rows,
  prepareRow,
  } = useTable(
    {
      columns,
      data,
      sortTypes
    },
  useSortBy
);

Now you can add the custom function into your column definitions.

const columns: Column<SomeObject>[] = React.useMemo(() => 
  [
    { accessor: 'someColumnID', Header: 'Some Column', sortType:'customStringSort' },
  ],
  [],
);

Hope this helps!

--Edit: If you want to memoized the function, this works. Just replace customStringSort with customStringSortMemo where appropriate.

const customStringSort: any = React.useCallback((rowA: Row, rowB: Row, columnId: string, desc: boolean) => 
  {
  const defaultVal = desc ? 'AAAAAAAAAAAA' : 'ZZZZZZZZ';
  return (rowA.values[columnId] ?? defaultVal).localeCompare(rowB.values[columnId] ?? defaultVal);
  },
[]);
    
const customStringSortMemo = React.useMemo(() => customStringSort[customStringSort]);
RoofusEat
  • 99
  • 1
  • 7
  • 1
    regarding TS and `any`. You have the type in the wrong place. It should be after the params. E.g. `const customStringSort = (rowA: Row, rowB: Row, columnId: string, desc: boolean):number => {` – Kildareflare Jul 30 '21 at 01:01
1

The other answers here are alright, but I felt a runnable example would be useful.

So how does one supply a sortType function?

sortType expects a function. It's up to you if you want to memoize that function or just inline a function, but the docs want you to memoize it. Here's a codesandbox showing it: https://codesandbox.io/s/react-table-v7-sortby-custom-sort-1d01qv?file=/src/App.js

Plumpie
  • 426
  • 1
  • 6
  • 22
-1

Custom Sort ini Last Header in const columns

const columns = React.useMemo(
            () => [
              {
                Header: 'ID',
                accessor: 'IDXX_GRPX'
              },
              {
                Header: 'Nama Group',
                accessor: 'NAMA_GRPX'
              },
              {
                Header: 'Kecamatan',
                accessor: 'KECX_DESC'
              },
              {
                Header: 'Kelurahan',
                accessor: 'AREA_DESC'
              },
              {
                Header: 'Unit',
                accessor: 'BUSS_CODE'
              },
              {
                Header: 'Kode Urut',
                accessor: 'KODE_URUT',
                sortType: (a, b) => {
                      if (a.values['KODE_URUT'] < b.values['KODE_URUT']) {
                          return -1;
                      } else if (a.values['KODE_URUT'] > b.values['KODE_URUT']) {
                          return 1;
                      };
    
                      return 0;
                  }
              }
            ],
            []
          );
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 06 '22 at 18:31
  • 1
    This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31225845) – MD. RAKIB HASAN Mar 11 '22 at 08:19