React Table Tutorial: useFilter (Part 2)

In the previous blogpost of React Table tutorial series, we learned how to get started with React-table-tutorial-7 and built a demo application using hook useTable. Adding more to the react-table tutorial series, we will now pick another hook, useFilter, and implement it in the same demo app, that we’ve built earlier.

So, I’d suggest going through my previous blog, React Table Tutorial Part-1: Project Setup and useTable, if you want to build the application from scratch; here, we will only implement the useFilter hook and explore a bit about the same.

Overview

Remember the previous way of implementing the react table filter? It was kind of messy and too much to take care of. But, thanks to react-table v7 for making it more accessible, handy, and customizable. The headless version of the react-table gives you control for designing and maintaining the table as per your requirements. Also, new features of the react-table make the React table library lightweight and give you opportunities to improve performance.

React Table Tutorial Goal: useFilter hook

Getting started with React Table useFilter hook

Once, done with setting up the project with the useTable hook, follow these steps to implement useFilter and useGlobalFilter in your project.

According to our UI, this will be the project structure for the react-table example.

React table tutorial

We will implement the default filter and select column filter. For that, we will

  • Update App.js & TableContainer.js
  • Create a new file named – Filter.js (which will have functional components for Filter views)

Without further ado, let’s create Filter.js and define React table components for Filter UI.

Defining Filter Components for UI

In this React Table demo, we will implement three filter views –

  • Default Filter for Columns: It will render text input, and the column data is filtered based on the text entered.
  • Global Filter: It will render text input but not just for columns; the entire table data is filtered based on the text entered.
  • Select Filter for Column: It will render select input, and the column data is filtered based on the option selected from the list.

We will create a common file named Filter.js (or with any suitable name) from where we will export the above-mentioned functional components for readability purposes.

Filter.js

import { React, useMemo, useState } from "react";
import { useAsyncDebounce } from "react-table";
import { Label, Input } from "reactstrap";
 
// Component for Global Filter
export function GlobalFilter({ 
   globalFilter, 
   setGlobalFilter 
}) {
 const [value, setValue] = useState(globalFilter);
 
 const onChange = useAsyncDebounce((value) => {
   setGlobalFilter(value || undefined);
 }, 200);
 
 return (
   <div>
     <Label>Search Table: </Label>
     <Input
       value={value || ""}
       onChange={(e) => {
         setValue(e.target.value);
         onChange(e.target.value);
       }}
       placeholder=" Enter value "
       className="w-25"
       style={{
         fontSize: "1.1rem",
         margin: "15px",
         display: "inline",
       }}
     />
   </div>
 );
}
 
// Component for Default Column Filter
export function DefaultFilterForColumn({
 column: {
   filterValue,
   preFilteredRows: { length },
   setFilter,
 },
}) {
 return (
   <Input
     value={filterValue || ""}
     onChange={(e) => {
       // Set undefined to remove the filter entirely
       setFilter(e.target.value || undefined);
     }}
     placeholder={`Search ${length} records..`}
     style={{ marginTop: "10px" }}
   />
 );
}
 
// Component for Custom Select Filter
export function SelectColumnFilter({
 column: { filterValue, setFilter, preFilteredRows, id },
}) {
 
 // Use preFilteredRows to calculate the options
 const options = useMemo(() => {
   const options = new Set();
   preFilteredRows.forEach((row) => {
     options.add(row.values[id]);
   });
   return [...options.values()];
 }, [id, preFilteredRows]);
 
 // UI for Multi-Select box
 return (
   <select
     value={filterValue}
     onChange={(e) => {
       setFilter(e.target.value || undefined);
     }}
   >
     <option value="">All</option>
     {options.map((option, i) => (
       <option key={i} value={option}>
         {option}
       </option>
     ))}
   </select>
 );
}

Explanation

  • What is the use of useAsyncDebounce? – React table provides useAsyncDebounce to avoid the multiple re-renders caused due to side-effects and to use the latest one. Back-to-back state side-effects take place that triggers multiple renders. So, rather than handling it manually, React Table provides a hook to debounce the rapid side-effects.

    Here, we have little data, so that we won’t realize the importance of useAsyncDebounce. But, consider, if we have thousands of data filtered, then the continuous state changes and side-effects are much costlier than this demo app.

    A good developer takes care of the performance even while coding for a demo application. Try avoiding trash coding.

  • The setfilter, filterValue, and preFilterRows are column properties used for specific columns. We have destructured the column prop and used them to get the filter values of respective columns.

Rendering Filter components

For GlobalFilter and DefaultFilterForColumn

Open TableContainer.js and Import components and hooks-

import { useTable, useFilters, useGlobalFilter } from "react-table";
import { GlobalFilter, DefaultFilterForColumn} from "./Filter";

Pass DefaultFilterForColumn to useTable hook as a default filter for all the columns. So by default, your columns will have these components as filters unless you provide a custom filter or disable the filter.

useTable(
  {
    columns,
    data,
    defaultColumn: { Filter: DefaultFilterForColumn },
  },
 useFilters,
 useGlobalFilter

Now for rendering the UI, use the following code-

{/* Rendering Global Filter */}
 <GlobalFilter
     preGlobalFilteredRows={preGlobalFilteredRows}
     globalFilter={state.globalFilter}
     setGlobalFilter={setGlobalFilter}
  />

 
{/* Rendering Default Column Filter */}
 <div>
   {column.canFilter ? column.render("Filter") : null}
</div>

Your entire TableContainer.js will look like this-

// TableContainer.js

import { React } from "react";
import { useTable, useFilters, useGlobalFilter } from "react-table";
import { GlobalFilter, DefaultColumnFilter } from "./Filter";
 
export default function Table({ columns, data }) {
  const {
   getTableProps,
   getTableBodyProps,
   headerGroups,
   rows,
   state,
   visibleColumns,
   prepareRow,
   setGlobalFilter,
   preGlobalFilteredRows,
 } = useTable(
   {
     columns,
     data,
     defaultColumn: { Filter: DefaultFilterForColumn },
   },
   useFilters,
   useGlobalFilter
 );
 
 return (
   <table {...getTableProps()}>
     <thead>
       <tr>
         <th
           colSpan={visibleColumns.length}
           style={{
             textAlign: "center",
           }}
         >
           {/* Rendering Global Filter */}
           <GlobalFilter
             preGlobalFilteredRows={preGlobalFilteredRows}
             globalFilter={state.globalFilter}
             setGlobalFilter={setGlobalFilter}
           />
         </th>
       </tr>
       {headerGroups.map((headerGroup) => (
         <tr {...headerGroup.getHeaderGroupProps()}>
           {headerGroup.headers.map((column) => (
             <th {...column.getHeaderProps()}>
               {column.render("Header")}
               {/* Rendering Default Column Filter */}
               <div>
                 {column.canFilter ? column.render("Filter") 
                  :null}
               </div>
             </th>
           ))}
         </tr>
       ))}
     </thead>
     <tbody {...getTableBodyProps()}>
       {rows.map((row, i) => {
         prepareRow(row);
         return (
           <tr {...row.getRowProps()}>
             {row.cells.map((cell) => {
               return <td {...cell.getCellProps()}>
           {cell.render("Cell")}
         </td>;
             })}
           </tr>
         );
       })}
     </tbody>
   </table>
 );
}

For using SelectColumnFilter,

  • Open App.js where we have defined the array of columns.
  • Import SelectColumnFilter.
  • Add Filter: SelectColumnFilter to a particular object of the column.
{
  Header: "Status",
  accessor: "show.status",
  Filter: SelectColumnFilter,
  filter: "includes",
},

If you disable filter on any particular column, add the following line of code-

{
  Header: "Premiered",
  accessor: "show.premiered",
  // disable the filter for particular column
  disableFilters: true, 
  Cell: ({ cell: { value } }) => value || "-",
},

How does Column Filter work in React Table?

Do you remember we added a line for implementing column filter-

<div>{column.canFilter ? column.render("Filter") : null}</div>

The “Filter” is the property in the column definition. It will render whichever component is given as a value to the Filter key.

Here we have used defaultcolumn, so no need to define Filter for all the columns but we have to define the Filter key for custom filters (e.g. SelectColumnFilter)

The condition of column.canFilter will be executed and it will render component defined to key Filter.

Here we have mentioned the custom filter, SelectColumnFilter, in the object and set DefaultFilterForColumn as a default shown filter.

The entire source code is available at Github Repository. Feel free to clone and play around with the code.

Conclusion

So, this was about how to filter your table data using the useFilter hook. I hope the React Table tutorial was helpful for you. If you have any questions or suggestions, feel free to comment below.

If you are a ReactJs enthusiast, check the ReactJS Tutorials page with more tutorials and its respective github repository.

At Bacancy Technology, developers try their best to come up with optimum solutions and affordable problem-solving approaches. If you are looking for a helping hand for your project, contact us to hire ReactJS developers without wasting a minute.

Frequently Asked Questions (FAQs)

How to count rows and columns in a react-table?

You can use props – rows and columns to know row and column numbers respectively. Here’s the code snippet if that helps-

export default function Table({ columns, data }) {
 const {
   // other code
   rows,
   } = useTable(
   {
     columns,
     data,
   },
  );
 // row & column number 
 console.log(columns.length, data, rows.length);
How to resize the column width of a specific column in the react-table?

The easiest way to set or resize the column width of a specific column is to give width property to column definition and then use it while rendering the table. Here’s the code snippet-

const columns = [
   {
     Header: "Name",
     accessor: "show.name",
     width: 150,
   },
	...
]
 
...
{headerGroup.headers.map((column) => (
   <th
      {...column.getHeaderProps({ style: { 
        width: column.width } 
      })}
   >
))}
How to disable the filter for a specific column in the react-table?

If you want to disable the filter for a particular column then you can add this line of code in the column definition-

{
  Header: "Time",
  accessor: "time",
  // disable the filter for particular column
  disableFilters: true, 
},
X

Get Our Newsletter

Be The First To Get The Latest Updates And Tutorials.

Request A Free Consultation