You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tanstack/React Table自定义多条件过滤器失效问题排查

React Table自定义组合过滤器逻辑异常排查

问题背景

尝试通过多条件过滤器对象列表过滤人员数据,过滤器结构定义如下:

// 过滤器可选值
condition: { "WHERE", "AND", "OR" }
id: { "firstName", "lastName", "birth" }
operator: { "contains", "does not contain", "equals", "does not equal" }
value: ""

示例过滤器对象:

[
  { condition: 'WHERE', id: 'firstName', operator: "does not contain", value: "f" },
  { condition: 'AND', id: 'lastName', operator: "does not contain", value: "d" }
]

待过滤的人员列表:

[
  { firstName: 'foo', lastName: "bar" },
  { firstName: 'john', lastName: "doe" },
  { firstName: 'rick', lastName: "roll" }
]

预期仅返回{ firstName: 'rick', lastName: "roll" },但实际过滤结果不符合预期,过滤器有时生效有时结果相反。

现有代码

useReactTable配置

const table = useReactTable({
  data: people,
  columns: tableColumns,
  filterFns: filterData,
  enableFilters: true,
  state: {
    sorting: sort,
    columnFilters: filters,
    grouping: groupState,
    columnVisibility
  },
  onColumnFiltersChange: updateFilter,
  onGroupingChange: setGroupState,
  onSortingChange: setSort,
  onColumnVisibilityChange: setColumnVisibility,
  getExpandedRowModel: getExpandedRowModel(),
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getGroupedRowModel: getGroupedRowModel()
});

自定义过滤函数

function filterData(data) {
    return data.filter((item) => {
      let result = true;
      filters.forEach((filter) => {
        let fieldValue = item[filter.id.toLowerCase().replace(/\s/g, '_')];
        let condition = false;
        switch (filter.operator) {
          case 'Contains':
            condition = fieldValue.toLowerCase().includes(filter.value.toLowerCase());
            break;
          case 'Does not contain':
            condition = !fieldValue.toLowerCase().includes(filter.value.toLowerCase());
            break;
          case 'Starts with':
            condition = fieldValue.toLowerCase().startsWith(filter.value.toLowerCase());
            break;
          case 'Ends with':
            condition = fieldValue.toLowerCase().endsWith(filter.value.toLowerCase());
            break;
          case 'Equals':
            condition = fieldValue === filter.value;
            break;
          case 'Does not equal':
            condition = fieldValue !== filter.value;
            break;
          case 'Is empty':
            condition = fieldValue === '';
            break;
          case 'Is not empty':
            condition = fieldValue !== '';
            break;
          default:
            condition = true;
        }
        if (filter.condition === 'AND') {
          result = result && condition;
        } else if (filter.condition === 'OR') {
          result = result || condition;
        }
      });
      return result;
    });
  }

问题分析与修正

核心问题1:filterFns用法错误

React Table的filterFns是列级过滤函数集合,用于定义单个列的过滤逻辑,而非全局过滤函数。要实现多条件组合过滤(带WHERE/AND/OR),需要开启手动过滤模式,自行处理过滤后的数据:

// 先手动处理过滤逻辑,用useMemo缓存结果避免重复计算
const filteredData = useMemo(() => {
  if (filters.length === 0) return people;
  return filterData(people);
}, [people, filters]);

const table = useReactTable({
  data: filteredData, // 传入过滤后的数据
  columns: tableColumns,
  enableFilters: true,
  manualFiltering: true, // 开启手动过滤,禁用内置过滤逻辑
  state: {
    sorting: sort,
    columnFilters: filters,
    grouping: groupState,
    columnVisibility
  },
  onColumnFiltersChange: updateFilter,
  onGroupingChange: setGroupState,
  onSortingChange: setSort,
  onColumnVisibilityChange: setColumnVisibility,
  getExpandedRowModel: getExpandedRowModel(),
  getCoreRowModel: getCoreRowModel(),
  getSortedRowModel: getSortedRowModel(),
  getGroupedRowModel: getGroupedRowModel()
});

核心问题2:过滤逻辑的初始值与WHERE条件未处理

原代码中result初始值设为true,且未处理WHERE类型的过滤器,导致第一个过滤条件完全失效。正确逻辑应该是:

  • 以第一个WHERE过滤器的结果作为初始值
  • 后续过滤器通过AND/OR叠加条件

核心问题3:大小写匹配与字段名转换错误

  • 示例过滤器的operator是小写开头(如"does not contain"),但代码中case是大写开头('Does not contain'),导致匹配失败进入default
  • 字段名转换冗余:filter.id是firstName,转换后变成firstname,与人员对象的字段名不匹配,导致fieldValue为undefined引发错误

修正后的过滤函数

function filterData(data) {
  if (filters.length === 0) return data;

  return data.filter((item) => {
    // 处理第一个WHERE条件作为初始结果
    let result = false;
    const firstFilter = filters[0];
    if (firstFilter.condition === 'WHERE') {
      result = getConditionResult(item, firstFilter);
    }

    // 处理后续的AND/OR条件
    for (let i = 1; i < filters.length; i++) {
      const filter = filters[i];
      const condition = getConditionResult(item, filter);
      
      if (filter.condition === 'AND') {
        result = result && condition;
      } else if (filter.condition === 'OR') {
        result = result || condition;
      }
    }

    return result;
  });
}

// 抽离条件判断逻辑,简化维护
function getConditionResult(item, filter) {
  const fieldValue = item[filter.id];
  if (fieldValue == null) return false; // 处理字段不存在的情况

  const lowerField = fieldValue.toLowerCase();
  const lowerValue = filter.value.toLowerCase();

  switch (filter.operator.toLowerCase()) { // 统一转小写,避免大小写敏感
    case 'contains':
      return lowerField.includes(lowerValue);
    case 'does not contain':
      return !lowerField.includes(lowerValue);
    case 'starts with':
      return lowerField.startsWith(lowerValue);
    case 'ends with':
      return lowerField.endsWith(lowerValue);
    case 'equals':
      return fieldValue === filter.value; // 精确匹配不转小写
    case 'does not equal':
      return fieldValue !== filter.value;
    case 'is empty':
      return fieldValue === '';
    case 'is not empty':
      return fieldValue !== '';
    default:
      return true;
  }
}

修正点说明

  1. 开启manualFiltering,自行处理全局过滤逻辑,避免React Table内置过滤的干扰
  2. 以第一个WHERE条件作为过滤初始值,不再用true默认值
  3. 统一operator的大小写匹配,避免因大小写不一致导致的过滤失效
  4. 去掉冗余的字段名转换,保证能正确获取目标字段值
  5. 抽离条件判断逻辑,简化代码维护,增加字段不存在的容错处理

内容的提问来源于stack exchange,提问作者Josh Antoniuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:14:53