列过滤指南

示例

想要跳转到实现?查看这些示例:

API

列过滤 API

列过滤指南

过滤有两种类型:列过滤和全局过滤。

本指南将专注于列过滤,这是应用于单个列访问器值的过滤器。

TanStack table 支持客户端和手动服务器端过滤。本指南将介绍如何实现和自定义这两种方式,并帮助您决定哪种最适合您的用例。

客户端 vs 服务器端过滤

如果您有大型数据集,您可能不希望将所有数据加载到客户端浏览器中进行过滤。在这种情况下,您很可能希望实现服务器端过滤、排序、分页等功能。

然而,正如在分页指南中也讨论的那样,许多开发者低估了在不影响性能的情况下可以在客户端加载多少行数据。TanStack table 示例经常测试处理多达 100,000 行或更多数据,在客户端过滤、排序、分页和分组方面具有良好的性能。这并不一定意味着您的应用程序能够处理那么多行,但如果您的表格最多只有几千行,您可能能够利用 TanStack table 提供的客户端过滤、排序、分页和分组功能。

TanStack Table 可以以良好的性能处理数千个客户端行。不要在没有仔细考虑的情况下就排除客户端过滤、分页、排序等功能。

每个用例都不同,将取决于表格的复杂性、您有多少列、每个数据片段有多大等等。需要注意的主要瓶颈是:

  1. 您的服务器能否在合理的时间(和成本)内查询所有数据?
  2. 获取的总大小是多少?(如果您没有很多列,这可能不会像您想象的那样扩展得很糟糕。)
  3. 如果一次加载所有数据,客户端浏览器是否使用了太多内存?

如果您不确定,您可以始终从客户端过滤和分页开始,然后随着数据的增长在未来切换到服务器端策略。

手动服务器端过滤

如果您已经决定需要实现服务器端过滤而不是���用内置的客户端过滤,以下是操作方法。

手动服务器端过滤不需要 getFilteredRowModel 表格选项。相反,您传递给表格的 data 应该已经被过滤。但是,如果您已经传递了 getFilteredRowModel 表格选项,您可以通过将 manualFiltering 选项设置为 true 来告诉表格跳过它。

jsx
const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  // getFilteredRowModel: getFilteredRowModel(), // 手动服务器端过滤不需要
  manualFiltering: true,
})
const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  // getFilteredRowModel: getFilteredRowModel(), // 手动服务器端过滤不需要
  manualFiltering: true,
})

注意: 使用手动过滤时,本指南其余部分讨论的许多选项将不起作用。当 manualFiltering 设置为 true 时,表格实例不会对传递给它的行应用任何过滤逻辑。相反,它将假设行已经被过滤,并按原样使用您传递给它的 data

客户端过滤

如果您使用内置的客户端过滤功能,首先需要向表格选项传递 getFilteredRowModel 函数。每当表格需要过滤数据时,都会调用此函数。您可以从 TanStack Table 导入默认的 getFilteredRowModel 函数,也可以创建自己的函数。

jsx
import { useReactTable, getFilteredRowModel } from '@tanstack/react-table'
//...
const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(), // 客户端过滤需要
})
import { useReactTable, getFilteredRowModel } from '@tanstack/react-table'
//...
const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(), // 客户端过滤需要
})

列过滤状态

无论您使用客户端还是服务器端过滤,都可以利用 TanStack Table 提供的内置列过滤状态管理。有许多表格和列 API 可以变更和交互过滤状态以及检索列过滤状态。

列过滤状态定义为具有以下形状的对象数组:

ts
interface ColumnFilter {
  id: string
  value: unknown
}
type ColumnFiltersState = ColumnFilter[]
interface ColumnFilter {
  id: string
  value: unknown
}
type ColumnFiltersState = ColumnFilter[]

由于列过滤状态是对象数组,您可以同时应用多个列过滤器。

访问列过滤状态

您可以像使用 table.getState() API 访问任何其他表格状态一样,从表格实例访问列过滤状态。

jsx
const table = useReactTable({
  columns,
  data,
  //...
})

console.log(table.getState().columnFilters) // 从表格实例访问列过滤状态
const table = useReactTable({
  columns,
  data,
  //...
})

console.log(table.getState().columnFilters) // 从表格实例访问列过滤状态

但是,如果您需要在表格初始化之前访问列过滤状态,您可以像下面那样"控制"列过滤状态。

受控列过滤状态

如果您需要轻松访问列过滤状态,可以使用 state.columnFiltersonColumnFiltersChange 表格选项在自己的状态管理中控制/管理列过滤状态。

tsx
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]) // 可以在这里设置初始列过滤状态
//...
const table = useReactTable({
  columns,
  data,
  //...
  state: {
    columnFilters,
  },
  onColumnFiltersChange: setColumnFilters,
})
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]) // 可以在这里设置初始列过滤状态
//...
const table = useReactTable({
  columns,
  data,
  //...
  state: {
    columnFilters,
  },
  onColumnFiltersChange: setColumnFilters,
})

初始列过滤状态

如果您不需要在自己的状态管理或作用域中控制列过滤状态,但仍想设置初始列过滤状态,可以使用 initialState 表格选项而不是 state

jsx
const table = useReactTable({
  columns,
  data,
  //...
  initialState: {
    columnFilters: [
      {
        id: 'name',
        value: 'John', // 默认按 'John' 过滤 name 列
      },
    ],
  },
})
const table = useReactTable({
  columns,
  data,
  //...
  initialState: {
    columnFilters: [
      {
        id: 'name',
        value: 'John', // 默认按 'John' 过滤 name 列
      },
    ],
  },
})

注意: 不要同时使用 initialState.columnFiltersstate.columnFilters,因为 state.columnFilters 中的初始化状态将覆盖 initialState.columnFilters

FilterFns

每个列都可以有自己独特的过滤逻辑。从 TanStack Table 提供的任何过滤函数中选择,或创建自己的过滤函数。

默认情况下,有 10 个内置过滤函数可供选择:

  • includesString - 不区分大小写的字符串包含
  • includesStringSensitive - 区分大小写的字符串包含
  • equalsString - 不区分大小写的字符串相等
  • equalsStringSensitive - 区分大小写的字符串相等
  • arrIncludes - 数组内的项目包含
  • arrIncludesAll - 数组中包含所有项目
  • arrIncludesSome - 数组中包含某些项目
  • equals - 对象/引用相等 Object.is/===
  • weakEquals - 弱对象/引用相等 ==
  • inNumberRange - 数字范围包含

您还可以将自定义过滤函数定义为 filterFn 列选项,或使用 filterFns 表格选项作为全局过滤函数。

自定义过滤函数

注意: 这些过滤函数仅在客户端过滤期间运行。

filterFn 列选项或 filterFns 表格选项中定义自定义过滤函数时,它应该具有以下签名:

ts
const myCustomFilterFn: FilterFn = (row: Row, columnId: string, filterValue: any, addMeta: (meta: any) => void) => boolean
const myCustomFilterFn: FilterFn = (row: Row, columnId: string, filterValue: any, addMeta: (meta: any) => void) => boolean

每个过滤函数接收:

  • 要过滤的行
  • 用于检索行值的 columnId
  • 过滤值

并且如果行应该包含在过滤行中,应返回 true���如果应该删除,则返回 false

jsx
const columns = [
  {
    header: () => 'Name',
    accessorKey: 'name',
    filterFn: 'includesString', // 使用内置过滤函数
  },
  {
    header: () => 'Age',
    accessorKey: 'age',
    filterFn: 'inNumberRange',
  },
  {
    header: () => 'Birthday',
    accessorKey: 'birthday',
    filterFn: 'myCustomFilterFn', // 使用自定义全局过滤函数
  },
  {
    header: () => 'Profile',
    accessorKey: 'profile',
    // 直接使用自定义过滤函数
    filterFn: (row, columnId, filterValue) => {
      return // 基于您的自定义逻辑返回 true 或 false
    },
  }
]
//...
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  filterFns: { //添加自定义排序函数
    myCustomFilterFn: (row, columnId, filterValue) => { //在此处内联定义
      return // 基于您的自定义逻辑返回 true 或 false
    },
    startsWith: startsWithFilterFn, // 在其他地方定义
  },
})
const columns = [
  {
    header: () => 'Name',
    accessorKey: 'name',
    filterFn: 'includesString', // 使用内置过滤函数
  },
  {
    header: () => 'Age',
    accessorKey: 'age',
    filterFn: 'inNumberRange',
  },
  {
    header: () => 'Birthday',
    accessorKey: 'birthday',
    filterFn: 'myCustomFilterFn', // 使用自定义全局过滤函数
  },
  {
    header: () => 'Profile',
    accessorKey: 'profile',
    // 直接使用自定义过滤函数
    filterFn: (row, columnId, filterValue) => {
      return // 基于您的自定义逻辑返回 true 或 false
    },
  }
]
//...
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  filterFns: { //添加自定义排序函数
    myCustomFilterFn: (row, columnId, filterValue) => { //在此处内联定义
      return // 基于您的自定义逻辑返回 true 或 false
    },
    startsWith: startsWithFilterFn, // 在其他地方定义
  },
})
自定义过滤函数行为

您可以将一些其他属性附加到过滤函数以自定义其行为:

  • filterFn.resolveFilterValue - 任何给定 filterFn 上的这个可选"悬挂"方法允许过滤函数在将过滤值传递给过滤函数之前转换/清理/格式化过滤值。

  • filterFn.autoRemove - 任何给定 filterFn 上的这个可选"悬挂"方法会传递一个过滤值,并期望如果过滤值应该从过滤状态中删除则返回 true。例如,如果过滤值设置为 false,某些布尔样式过滤器可能希望从表格状态中删除过滤值。

tsx
const startsWithFilterFn = <TData extends MRT_RowData>(
  row: Row<TData>,
  columnId: string,
  filterValue: number | string, //resolveFilterValue 将把这个转换为字符串
) =>
  row
    .getValue<number | string>(columnId)
    .toString()
    .toLowerCase()
    .trim()
    .startsWith(filterValue); // 在 `resolveFilterValue` 中对过滤值进行 toString、toLowerCase 和 trim

// 如果过滤值为假值(在这种情况下为空字符串),则从过滤状态中删除过滤值
startsWithFilterFn.autoRemove = (val: any) => !val; 

// 在将过滤值传递给过滤函数之前转换/清理/格式化过滤值
startsWithFilterFn.resolveFilterValue = (val: any) => val.toString().toLowerCase().trim(); 
const startsWithFilterFn = <TData extends MRT_RowData>(
  row: Row<TData>,
  columnId: string,
  filterValue: number | string, //resolveFilterValue 将把这个转换为字符串
) =>
  row
    .getValue<number | string>(columnId)
    .toString()
    .toLowerCase()
    .trim()
    .startsWith(filterValue); // 在 `resolveFilterValue` 中对过滤值进行 toString、toLowerCase 和 trim

// 如果过滤值为假值(在这种情况下为空字符串),则从过滤状态中删除过滤值
startsWithFilterFn.autoRemove = (val: any) => !val; 

// 在将过滤值传递给过滤函数之前转换/清理/格式化过滤值
startsWithFilterFn.resolveFilterValue = (val: any) => val.toString().toLowerCase().trim(); 

自定义列过滤

有很多表格和列选项可以用来进一步自定义列��滤行为。

禁用列过滤

默认情况下,所有列都启用列过滤。您可以使用 enableColumnFilters 表格选项或 enableColumnFilter 列选项为所有列或特定列禁用列过滤。您还可以通过将 enableFilters 表格选项设置为 false 来关闭列过滤和全局过滤。

为列禁用列过滤将导致该列的 column.getCanFilter API 返回 false

jsx
const columns = [
  {
    header: () => 'Id',
    accessorKey: 'id',
    enableColumnFilter: false, // 为此列禁用列过滤
  },
  //...
]
//...
const table = useReactTable({
  columns,
  data,
  enableColumnFilters: false, // 为所有列禁用列过滤
})
const columns = [
  {
    header: () => 'Id',
    accessorKey: 'id',
    enableColumnFilter: false, // 为此列禁用列过滤
  },
  //...
]
//...
const table = useReactTable({
  columns,
  data,
  enableColumnFilters: false, // 为所有列禁用列过滤
})

过滤子行(展开)

在使用展开、分组和聚合等功能时,有一些额外的表格选项可以自定义列过滤的行为。

从叶行过滤

默认情况下,过滤是从父行向下进行的,因此如果父行被过滤掉,其所有子行也会被过滤掉。根据您的用例,如果您只希望用户搜索顶级行而不是子行,这可能是所需的行为。这也是性能最佳的选项。

但是,如果您希望允许子行被过滤和搜索,无论父行是否被过滤掉,您可以将 filterFromLeafRows 表格选项设置为 true。将此选项设置为 true 将导致从叶行向上进行过滤,这意味着只要父行的子行或孙行中有一个也被包含,父行就会被包含。

jsx
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  filterFromLeafRows: true, // 过滤和搜索子行
})
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  filterFromLeafRows: true, // 过滤和搜索子行
})
最大叶行过滤深度

默认情况下,对树中的所有行进行过滤,无论它们是根级父行还是父行的子叶行。将 maxLeafRowFilterDepth 表格选项设置为 0 将导致过滤仅应用于根级父行,所有子行保持未过滤状态。类似地,将此选项设置为 1 将导致过滤仅应用于 1 级深度的子叶行,依此类推。

如果您希望在父行通过过滤器时保留父行的子行不被过滤掉,请使用 maxLeafRowFilterDepth: 0

jsx
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  maxLeafRowFilterDepth: 0, // 仅过滤掉根级父行
})
const table = useReactTable({
  columns,
  data,
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getExpandedRowModel: getExpandedRowModel(),
  maxLeafRowFilterDepth: 0, // 仅过滤掉根级父行
})

列过滤 APIs

有很多列和表格 API 可以用来与列过滤状态交互并连接到您的 UI 组件。以下是可用 API 及其最常见用例的列表:

  • table.setColumnFilters - 用新状态覆盖整个列过滤状态

  • table.resetColumnFilters - 对"清除所有/重置过滤器"按钮很有用

  • column.getFilterValue - 对于获取输入的默认初始过滤值,或甚至直接向过滤输入提供过滤值很有用

  • column.setFilterValue - 对于将过滤输入连接到其 onChangeonBlur 处理程序很有用

  • column.getCanFilter - 对于禁用/启用过滤输入很有用

  • column.getIsFiltered - 对于显示列当前正在被过滤的视觉指示器很有用

  • column.getFilterIndex - 对于显示当前过滤器应用的顺序很有用

  • column.getAutoFilterFn -

  • column.getFilterFn - 对于显示当前使用的过滤模式或函数很有用