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

shadcn DataTableFacetedFilter报错filterValue.toLowerCase不是函数求解决

问题分析

TypeError: filterValue.toLowerCase is not a function 错误的核心原因是:你通过DataTableFacetedFilter设置的filterValue是数组类型,但表格列默认的文本筛选逻辑会尝试调用toLowerCase()方法,而数组没有这个方法,导致报错。同时数据筛选失效,是因为列的筛选函数未适配数组类型的筛选值。

解决方案

1. 适配列的筛选函数,支持数组筛选值

在你的表格列定义中,为使用DataTableFacetedFilter的列添加自定义筛选函数,让它能处理数组类型的filterValue:

const columns = [
  // 替换为你的实际列配置
  {
    accessorKey: "targetColumnKey",
    header: "列标题",
    // 自定义筛选逻辑:判断行值是否在筛选数组中
    filterFn: (row, columnId, filterValue) => {
      if (!filterValue || filterValue.length === 0) return true;
      return filterValue.includes(row[columnId]);
    },
    // 绑定筛选组件
    meta: {
      filter: ({ column }) => (
        <DataTableFacetedFilter
          column={column}
          title="筛选标题"
          options={[
            { label: "选项1", value: "value1" },
            { label: "选项2", value: "value2" },
          ]}
        />
      ),
    },
  },
];

如果使用TanStack Table的内置函数,也可以直接指定filterFn: "arrIncludes"(需确保版本支持该内置函数)。

2. 修复筛选值初始化逻辑

当前代码未处理column.getFilterValue()返回undefined的情况,可能导致Set初始化异常,修改如下:

// 原代码
const selectedValues = new Set(column?.getFilterValue());
// 修改为
const selectedValues = new Set(column?.getFilterValue() ?? []);

3. 启用筛选选项的搜索功能(可选)

组件中的CommandInput仅作为占位符,未实现搜索功能,添加逻辑让用户可以搜索筛选选项:

export function DataTableFacetedFilter({ column, title, options }) {
  const facets = column?.getFacetedUniqueValues();
  const selectedValues = new Set(column?.getFilterValue() ?? []);
  const [searchQuery, setSearchQuery] = React.useState("");

  // 根据搜索关键词过滤选项
  const filteredOptions = options.filter((option) =>
    option.label.toLowerCase().includes(searchQuery.toLowerCase())
  );

  return (
    <Popover>
      {/* 保留原触发按钮代码 */}
      <PopoverTrigger asChild>
        <Button variant="outline" size="sm" className="h-8 border-dashed">
          <PlusCircleIcon className="mr-2 h-4 w-4" />
          {title}
          {/* 保留原选中状态展示代码 */}
          {selectedValues?.size > 0 && (
            <>
              <Separator orientation="vertical" className="mx-2 h-4" />
              <Badge
                variant="secondary"
                className="rounded-sm px-1 font-normal lg:hidden"
              >
                {selectedValues.size}
              </Badge>
              <div className="hidden space-x-1 lg:flex">
                {selectedValues.size > 2 ? (
                  <Badge
                    variant="secondary"
                    className="rounded-sm px-1 font-normal"
                  >
                    {selectedValues.size} selected
                  </Badge>
                ) : (
                  options
                    .filter((option) => selectedValues.has(option.value))
                    .map((option) => (
                      <Badge
                        variant="secondary"
                        key={option.value}
                        className="rounded-sm px-1 font-normal"
                      >
                        {option.label}
                      </Badge>
                    ))
                )}
              </div>
            </>
          )}
        </Button>
      </PopoverTrigger>
      <PopoverContent className="w-[200px] p-0" align="start">
        <Command>
          {/* 绑定搜索输入状态 */}
          <CommandInput 
            placeholder={title} 
            value={searchQuery}
            onValueChange={setSearchQuery}
          />
          <CommandList>
            <CommandEmpty>No results found.</CommandEmpty>
            <CommandGroup>
              {/* 使用过滤后的选项列表 */}
              {filteredOptions.map((option) => {
                const isSelected = selectedValues.has(option.value);
                return (
                  <CommandItem
                    key={option.value}
                    onSelect={() => {
                      if (isSelected) {
                        selectedValues.delete(option.value);
                      } else {
                        selectedValues.add(option.value);
                      }
                      const filterValues = Array.from(selectedValues);
                      column?.setFilterValue(
                        filterValues.length ? filterValues : undefined
                      );
                    }}
                  >
                    <div
                      className={cn(
                        "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
                        isSelected
                          ? "bg-primary text-primary-foreground"
                          : "opacity-50 [&_svg]:invisible"
                      )}
                    >
                      <Check className={cn("h-4 w-4")} />
                    </div>
                    {option.icon && (
                      <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
                    )}
                    <span>{option.label}</span>
                    {facets?.get(option.value) && (
                      <span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
                        {facets.get(option.value)}
                      </span>
                    )}
                  </CommandItem>
                );
              })}
            </CommandGroup>
            {selectedValues.size > 0 && (
              <>
                <CommandSeparator />
                <CommandGroup>
                  <CommandItem
                    onSelect={() => column?.setFilterValue(undefined)}
                    className="justify-center text-center"
                  >
                    Clear filters
                  </CommandItem>
                </CommandGroup>
              </>
            )}
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}
验证步骤
  1. 确认所有使用DataTableFacetedFilter的列都配置了正确的filterFn。
  2. 重新运行项目,检查TypeError是否消失。
  3. 选择筛选选项,验证表格数据是否按预期过滤。

内容的提问来源于stack exchange,提问作者Prajjwal Rajput

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:30:46