This week I needed to filter a table made with React Table. React table is great for its extensibility and so has hooks for this: useFilters and useGlobalFilter. useFilter
is for filtering by an individual column and useGlobalFilter
for filtering by content in any column.
However I needed to filter two columns by a single input. My table is for ingredients and has columns for: code, name, price per unit, supplier, etc. I need to filter by name and code. I couldn't use useFilters
as that would do an intersection (i.e. the search query would need to be in both the code and name row).
See the end Code Sandbox at: https://codesandbox.io/s/fuzzy-text-global-filter-rcy1f?file=/src/Table.tsx
Filtering Prepsheets.com Ingredients by Code
Thankfully, it's possible to do this with useGlobalFilter
.
import {
...,
useGlobalFilter,
} from 'react-table'
...
const {
...,
setGlobalFilter,
} = useTable(
{
...
},
...
useGlobalFilter,
);
See Code Sandbox: https://codesandbox.io/s/all-columns-global-filter-buof9?file=/src/Table.tsx
Awesome, by using setGlobalFilter
we can now filter by the content in any column. To restrict which columns we filter we'll specify a custom globalFilter
function.
import {
...
useGlobalFilter,
Row, // Typescript
IdType, // Typescript
}
import React, { ..., useCallback } = 'react'
...
const ourGlobalFilterFunction = useCallback(
// This is Typescript if you're using JS remove the types (e.g. :string)
(rows: Row<T>[], ids: IdType<T>[], query: string) => {
return rows.filter((row) =>
row.values['code'].includes(query) ||
row.values['name'].includes(query)
);
},
[],
);
const {
...
setGlobalFilter,
} = useTable(
{
globalFilter: ourGlobalFilterFunction
},
...
useGlobalFilter,
);
See Code Sandbox: https://codesandbox.io/s/specific-columns-global-filter-n1k4v?file=/src/Table.tsx
However, in my case this is inside a Table
component which I use in a couple of places, so let's make ourGlobalFilterFunction
take arbitrary column names for filtering. We'll also pass the filter query as a prop to Table
.
interface TableProps {
filters: string[];
filter: string;
}
const Table: React.FC<TableProps> = ({
filters,
filter,
}): React.ReactComponent => {
const ourGlobalFilterFunction = useCallback(
// This is Typescript if you're using JS remove the types (e.g. :string)
(rows: Row<T>[], ids: IdType<T>[], query: string) => {
return rows.filter((row) =>
for (const filter of filters) {
return row.values[filter].includes(query)
}
);
},
[filters],
);
const {
...
setGlobalFilter,
} = useTable(
{
globalFilter: ourGlobalFilterFunction
},
...
useGlobalFilter,
);
useEffect(() => {
setGlobalFilter(filter) // Set the Global Filter to the filter prop.
}, [filter, setGlobalFilter]);
return (
...
);
}
Code Sandbox: https://codesandbox.io/s/filter-props-global-filter-i18bd?file=/src/Table.tsx
Finally, I'd like this to do fuzzy text filtering. We'll use the match-sorter library for this
npm install match-sorter
import { matchSorter } from 'match-sorter';
...
const globalFilter = useCallback(
(rows: Row<T>[], ids: IdType<T>[], query: string) => {
return matchSorter(rows, query, {
keys: filters.map((columnName) => `values.${columnName}`),
});
},
[filters],
);
Code Sandbox: https://codesandbox.io/s/fuzzy-text-global-filter-rcy1f?file=/src/Table.tsx
Top comments (1)
Thank you for this