react-table

React table - Dropdown filter


I have implemented Global Filer using setGlobalFilter

enter image description here

But I want to Set filter column wise like:

Outside the table, I need a dropdown with some values which should filter the react-table based on the opted value from the dropdown. this dropdown filter should filter out the whole table based on the opted value from the dropdown.

enter image description here

Can anyone give me link of demo or any help regarding this will be appreciated.


Solution

  • Ok, let's do it on react-table v8, yes, it is the new version of react-table.

    First, make sure you import the required items from @tanstack/react-table

    import {
      createColumnHelper,
      flexRender,
      getCoreRowModel,
      getFilteredRowModel,
      useReactTable,
    } from "@tanstack/react-table";
    

    Here, we use simple data.

    const defaultData = [
      {
        firstName: "tanner",
        lastName: "linsley",
        age: 24,
        visits: 100,
        status: "In Relationship",
        progress: 50,
      },
      {
        firstName: "tandy",
        lastName: "miller",
        age: 40,
        visits: 40,
        status: "Single",
        progress: 80,
      },
      {
        firstName: "joe",
        lastName: "dirte",
        age: 45,
        visits: 20,
        status: "Complicated",
        progress: 10,
      },
    ];
    

    In the new version of react-table, we do not have to memoize the columns. We can define it with the help of columnHelper. You can read the details here.

    
    const columnHelper = createColumnHelper();
    
    const columns = [
      columnHelper.accessor("firstName", {
        header: "First Name",
      }),
      columnHelper.accessor("lastName", {
        header: "Last Name",
      }),
      columnHelper.accessor("age", {
        header: "Age",
      }),
      columnHelper.accessor("visits", {
        header: "Visits",
      }),
      columnHelper.accessor("status", {
        header: "Status",
      }),
      columnHelper.accessor("progress", {
        header: "Profile Progress",
      }),
    ];
    

    Next, we define required states at our component.

      const [data] = useState(() => [...defaultData]);
    
      // to keep the selected column field
      const [field, setField] = useState();
    
      // to keep the input search value
      const [searchValue, setSearchValue] = useState("");
    
      // required by react-table for filtering purposes
      const [columnFilters, setColumnFilters] = useState();
    

    In the previous version, we use useTable hooks to create our table instance, here, in the new version, we use useReactTable instead. We pass these configurations to make our filter run correctly.

      const table = useReactTable({
        data,
        columns,
        enableFilters: true,
        enableColumnFilters: true,
        getCoreRowModel: getCoreRowModel(),
        getFilteredRowModel: getFilteredRowModel(),
        state: {
          columnFilters,
        },
        onColumnFiltersChange: setColumnFilters,
      });
    

    Next, we create our select option tag where we bind the select option value to field state and the onChange event handler to handleSelectChange. For the input tag, we bind the value to searchValue and and the onChange event handler to handleInputChange method. Inside the select change handler, we need to reset both columnFilters and searchValue states.

      ...
    
      const handleSelectChange = (e) => {
        setColumnFilters([]);
        setSearchValue("");
        setField(e.target.value);
      };
    
      const handleInputChange = (e) => {
        setSearchValue(e.target.value);
      };
    
      return (
        ...
          <select value={field} onChange={handleSelectChange}>
            <option value="">Select Field</option>
            {table.getAllLeafColumns().map((column, index) => {
              return (
                <option value={column.id} key={index}>
                  {column.columnDef.header}
                </option>
              );
            })}
          </select>
          <input
            value={searchValue}
            onChange={handleInputChange}
            className="p-2 font-lg shadow border border-block"
            placeholder={
              field ? `Search ${field} column...` : "Please select a field"
            }
          />
       ...
     )
    

    Here, we got the select options list from table.getAllLeafColumns().

    Since columns age, visits, and progress values are numbers, we need to modify our columns configurations by using custom filterFn options.

    const columns = [
      ...
    
      columnHelper.accessor("age", {
        header: "Age",
        filterFn: (row, _columnId, value) => {
          return row.original.age === parseInt(value);
        },
      }),
      columnHelper.accessor("visits", {
        header: "Visits",
        filterFn: (row, _columnId, value) => {
          return row.original.visits === parseInt(value);
        },
      }),
    
      ...
    
      columnHelper.accessor("progress", {
        header: "Profile Progress",
        filterFn: (row, _columnId, value) => {
          return row.original.progress === parseInt(value);
        },
      }),
    ];
    

    And as the documentation said that we need to remember this:

    Every filter function receives:

    1. The row to filter
    2. The columnId to use to retrieve the row's value
    3. The filter value

    and should return true if the row should be included in the filtered rows, and false if it should be removed.

    Finally, it is time to render the table:

      return (
        <div className="p-2">
          ...
    
          <table>
            <thead>
              {table.getHeaderGroups().map((headerGroup) => (
                <tr key={headerGroup.id}>
                  {headerGroup.headers.map((header) => (
                    <th key={header.id}>
                      {header.isPlaceholder
                        ? null
                        : flexRender(
                            header.column.columnDef.header,
                            header.getContext()
                          )}
                    </th>
                  ))}
                </tr>
              ))}
            </thead>
            <tbody>
              {table.getRowModel().rows.map((row) => (
                <tr key={row.id}>
                  {row.getVisibleCells().map((cell) => (
                    <td key={cell.id}>
                      {flexRender(cell.column.columnDef.cell, cell.getContext())}
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      );
    

    And here is the working code:

    Edit sleepy-poitras-6brjuc

    I hope it helps.