Global Filter in react-table v8 isn't working
Asked Answered
B

3

6

I have a slightly modified implementation of the react-table v8 filters: https://tanstack.com/table/v8/docs/examples/react/filters

In the original they seem to use a custom filter function to filter the table globally but it requires another library that i don't want to include, the documentation isn't very clear about this but there seems to be built-in functions that i can use: https://tanstack.com/table/v8/docs/api/features/filters

However the table stops filtering as soon as i change it, i tried not including the globalFilterFn as well as setting it to globalFilterFn: "includesString" which is one of the built-in functions i mentioned but nothing has worked so far.

here is my code:

import React from "react";
import ReactDOM from "react-dom/client";

import "./index.css";

import {
  useReactTable,
  getCoreRowModel,
  getFilteredRowModel,
  getPaginationRowModel,
  getSortedRowModel,
  FilterFn,
  ColumnDef,
  flexRender
} from "@tanstack/react-table";

//import { RankingInfo, rankItem } from "@tanstack/match-sorter-utils";

import { makeData, Person } from "./makeData";

/* declare module "@tanstack/table-core" {
  interface FilterMeta {
    itemRank: RankingInfo;
  }
}

const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
  // Rank the item
  const itemRank = rankItem(row.getValue(columnId), value);

  // Store the itemRank info
  addMeta({
    itemRank
  });

  // Return if the item should be filtered in/out
  return itemRank.passed;
}; */

function App() {
  const rerender = React.useReducer(() => ({}), {})[1];

  const [globalFilter, setGlobalFilter] = React.useState("");

  const columns = React.useMemo<ColumnDef<Person>[]>(
    () => [
      {
        header: "Name",
        footer: (props) => props.column.id,
        columns: [
          {
            accessorKey: "firstName",
            cell: (info) => info.getValue(),
            footer: (props) => props.column.id
          },
          {
            accessorFn: (row) => row.lastName,
            id: "lastName",
            cell: (info) => info.getValue(),
            header: () => <span>Last Name</span>,
            footer: (props) => props.column.id
          },
          {
            accessorFn: (row) => `${row.firstName} ${row.lastName}`,
            id: "fullName",
            header: "Full Name",
            cell: (info) => info.getValue(),
            footer: (props) => props.column.id
          }
        ]
      },
      {
        header: "Info",
        footer: (props) => props.column.id,
        columns: [
          {
            accessorKey: "age",
            header: () => "Age",
            footer: (props) => props.column.id
          },
          {
            header: "More Info",
            columns: [
              {
                accessorKey: "visits",
                header: () => <span>Visits</span>,
                footer: (props) => props.column.id
              },
              {
                accessorKey: "status",
                header: "Status",
                footer: (props) => props.column.id
              },
              {
                accessorKey: "progress",
                header: "Profile Progress",
                footer: (props) => props.column.id
              }
            ]
          }
        ]
      }
    ],
    []
  );

  const [data, setData] = React.useState(() => makeData(500));
  const refreshData = () => setData((old) => makeData(500));

  const table = useReactTable({
    data,
    columns,
    state: {
      globalFilter
    },
    onGlobalFilterChange: setGlobalFilter,
    //globalFilterFn: "includesString",
    getCoreRowModel: getCoreRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getPaginationRowModel: getPaginationRowModel()
  });

  return (
    <div className="p-2">
      <div>
        <input
          value={globalFilter ?? ""}
          onChange={(event) => setGlobalFilter(event.target.value)}
          className="p-2 font-lg shadow border border-block"
          placeholder="Search all columns..."
        />
      </div>
      <div className="h-2" />
      <table>
        <thead>
          {table.getHeaderGroups().map((headerGroup) => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map((header) => {
                return (
                  <th key={header.id} colSpan={header.colSpan}>
                    {header.isPlaceholder ? null : (
                      <>
                        <div
                          {...{
                            className: header.column.getCanSort()
                              ? "cursor-pointer select-none"
                              : "",
                            onClick: header.column.getToggleSortingHandler()
                          }}
                        >
                          {flexRender(
                            header.column.columnDef.header,
                            header.getContext()
                          )}
                          {{
                            asc: " 🔼",
                            desc: " 🔽"
                          }[header.column.getIsSorted() as string] ?? null}
                        </div>
                      </>
                    )}
                  </th>
                );
              })}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map((row) => {
            return (
              <tr key={row.id}>
                {row.getVisibleCells().map((cell) => {
                  return (
                    <td key={cell.id}>
                      {flexRender(
                        cell.column.columnDef.cell,
                        cell.getContext()
                      )}
                    </td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
      <div className="h-2" />
      <div className="flex items-center gap-2">
        <button
          className="border rounded p-1"
          onClick={() => table.setPageIndex(0)}
          disabled={!table.getCanPreviousPage()}
        >
          {"<<"}
        </button>
        <button
          className="border rounded p-1"
          onClick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
        >
          {"<"}
        </button>
        <button
          className="border rounded p-1"
          onClick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
        >
          {">"}
        </button>
        <button
          className="border rounded p-1"
          onClick={() => table.setPageIndex(table.getPageCount() - 1)}
          disabled={!table.getCanNextPage()}
        >
          {">>"}
        </button>
        <span className="flex items-center gap-1">
          <div>Page</div>
          <strong>
            {table.getState().pagination.pageIndex + 1} of{" "}
            {table.getPageCount()}
          </strong>
        </span>
        <span className="flex items-center gap-1">
          | Go to page:
          <input
            type="number"
            defaultValue={table.getState().pagination.pageIndex + 1}
            onChange={(e) => {
              const page = e.target.value ? Number(e.target.value) - 1 : 0;
              table.setPageIndex(page);
            }}
            className="border p-1 rounded w-16"
          />
        </span>
        <select
          value={table.getState().pagination.pageSize}
          onChange={(e) => {
            table.setPageSize(Number(e.target.value));
          }}
        >
          {[10, 20, 30, 40, 50].map((pageSize) => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>
      </div>
      <div>{table.getPrePaginationRowModel().rows.length} Rows</div>
      <div>
        <button onClick={() => rerender()}>Force Rerender</button>
      </div>
      <div>
        <button onClick={() => refreshData()}>Refresh Data</button>
      </div>
      <pre>{JSON.stringify(table.getState(), null, 2)}</pre>
    </div>
  );
}

const rootElement = document.getElementById("root");
if (!rootElement) throw new Error("Failed to find the root element");

ReactDOM.createRoot(rootElement).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

a link to CodeSanbox with it: https://codesandbox.io/s/long-monad-652jcm?file=/src/main.tsx

I'm still very inexperienced in React and even more with typescript so maybe i'm missing something obvious.

am i misinterpreting the docs, maybe the custom function is necessary?

Bobble answered 26/7, 2022 at 19:6 Comment(0)
S
2

There is a current issue on GitHub where you can't use a global filter if one of your column is using number fields. You can simply solve it by replacing the number by a string in your data. (.toString(), and sorting still works). If worked fine for me afterwards. :)

Shopper answered 18/8, 2022 at 9:43 Comment(0)
P
1

Your globalFilter doesn't do anything, you need to test if the contents of the table are equal to the filter or not.

Plasmo answered 26/7, 2022 at 19:16 Comment(2)
Yes, that is the problem, in the original example the used a custom function to filter the table, i was going to use the built-in functions since the custom one has a library i don't want, but when i change that the filter stops workingBobble
wait for real? If that's the case then it explains why it's not been working for meHarwilll
T
0

In react table, we can use filter function to filter columns their values being number type.

{
  accessorKey: "id",
  accessorFn: (row) => row?.id?.toString(),
  filterFn: (row, columnId, filterValue) => {
    console.log(row.getValue(columnId), filterValue);
    return row.getValue(columnId) === parseInt(filterValue);
  },
  header: ({ column }) => {
    return (
      <p
        onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
        className="flex items-center"
      >
        Id
        <ArrowUpDown className="ml-2 h-4 w-4" />
      </p>
    );
  },
  cell: ({ row }) => <p>{row.getValue("id")}</p>,
},
Tonality answered 8/8 at 6:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.