Sorting and Filtering the Data Yourself

View as Markdown
By default, the <DataSource /> sorts and filters the data in the browser - for array data, for a Promise, for a data function, and also when sortInfo or filterValue are controlled.
When you sort or filter the data yourself - on a server, in a data-fetching library like TanStack Query, or in your own code - tell the <DataSource /> not to do it again, with shouldReloadData:
<DataSource<Developer>
  primaryKey="id"
  data={rows}
  sortInfo={sortInfo}
  onSortInfoChange={setSortInfo}
  filterValue={filterValue}
  onFilterValueChange={setFilterValue}
  shouldReloadData={{ sortInfo: true, filterValue: true }}
/>
  • with a data function, it's called again on every sort or filter change, with the sortInfo and filterValue in its params;
  • with array data, you pass a new array every time the sorting or the filtering changes (eg from onSortInfoChange and onFilterValueChange).
Either way, the rows are shown as they come in - they are not sorted or filtered again in the browser.
Grouping and pivoting are always done in the browser - unless you use lazyLoad, where the server returns the groups.

Reusing the built-in sorting and filtering#

To sort and filter the data the same way the <DataSource /> does, use the functions it uses:
  • multisort(sortInfo, array) - the built-in sorting for flat data (it sorts the array in place, so pass a copy);
  • defaultFilterFunction(param) - the built-in column filtering for one row. It needs the row, its index, the array, the primary key, and the column filters with their filter types - the default filterTypes are exported too.
import {
  defaultFilterFunction,
  filterTypes,
  multisort,
} from '@infinite-table/infinite-react';

function sortAndFilter(rows, sortInfo, filterValue) {
  const filtered = rows.filter((data, index, dataArray) =>
    defaultFilterFunction({
      data,
      index,
      dataArray,
      primaryKey: data.id,
      filterValue,
      filterTypes,
    }),
  );
  return multisort(sortInfo, filtered);
}
If you configure custom filterTypes on the <DataSource />, pass the same ones to defaultFilterFunction.

With TanStack Query#

The query key includes the sorting and the filtering, so every change runs a new query. Thanks to placeholderData: keepPreviousData, the previous rows stay on screen until the new ones arrive - and because the <DataSource /> doesn't sort or filter them itself, they don't change until the query resolves.
View Mode
Fork
import {
  DataSource,
  DataSourcePropFilterValue,
  DataSourceSingleSortInfo,
  InfiniteTable,
  InfiniteTableColumn,
  defaultFilterFunction,
  filterTypes,
  multisort,
} from '@infinite-table/infinite-react';
import {
  QueryClient,
  QueryClientProvider,
  keepPreviousData,
  useQuery,
} from '@tanstack/react-query';
import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  country: string;
  stack: string;
  age: number;
};

const developers: Developer[] = [
  { id: 1, firstName: 'Bob', country: 'USA', stack: 'backend', age: 30 },
  { id: 2, firstName: 'Alice', country: 'France', stack: 'frontend', age: 25 },
  { id: 3, firstName: 'Carl', country: 'USA', stack: 'full-stack', age: 40 },
  { id: 4, firstName: 'Dana', country: 'France', stack: 'backend', age: 35 },
  { id: 5, firstName: 'Eve', country: 'Germany', stack: 'frontend', age: 28 },
  { id: 6, firstName: 'Frank', country: 'Germany', stack: 'backend', age: 45 },
];

// pretend this runs on a server: it filters and sorts the rows,
// with the same functions the DataSource uses
function fetchDevelopers(
  sortInfo: DataSourceSingleSortInfo<Developer>[],
  filterValue: DataSourcePropFilterValue<Developer>,
) {
  const filtered = developers.filter((data, index, dataArray) =>
    defaultFilterFunction({
      data,
      index,
      dataArray,
      primaryKey: data.id,
      filterValue,
      filterTypes,
    }),
  );
  const rows = multisort(sortInfo, filtered);

  return new Promise<Developer[]>((resolve) => {
    setTimeout(() => resolve(rows), 600);
  });
}

const columns: Record<string, InfiniteTableColumn<Developer>> = {
  firstName: { field: 'firstName', header: 'First name' },
  country: { field: 'country' },
  stack: { field: 'stack' },
  age: { field: 'age', type: 'number' },
};

const queryClient = new QueryClient();
const noRows: Developer[] = [];

function Developers() {
  const [sortInfo, setSortInfo] = React.useState<
    DataSourceSingleSortInfo<Developer>[]
  >([]);
  const [filterValue, setFilterValue] = React.useState<
    DataSourcePropFilterValue<Developer>
  >([]);

  const { data, isFetching } = useQuery({
    queryKey: ['developers', sortInfo, filterValue],
    queryFn: () => fetchDevelopers(sortInfo, filterValue),
    placeholderData: keepPreviousData,
  });

  return (
    <>
      <p style={{ color: 'var(--infinite-cell-color)' }}>
        {isFetching ? 'Loading...' : 'Sort or filter a column'}
      </p>
      <DataSource<Developer>
        primaryKey="id"
        data={data ?? noRows}
        sortInfo={sortInfo}
        onSortInfoChange={setSortInfo}
        filterValue={filterValue}
        onFilterValueChange={setFilterValue}
        shouldReloadData={{ sortInfo: true, filterValue: true }}
      >
        <InfiniteTable<Developer> columns={columns} showColumnFilters />
      </DataSource>
    </>
  );
}

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Developers />
    </QueryClientProvider>