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> ); }
验证步骤
- 确认所有使用
DataTableFacetedFilter的列都配置了正确的filterFn。 - 重新运行项目,检查
TypeError是否消失。 - 选择筛选选项,验证表格数据是否按预期过滤。
内容的提问来源于stack exchange,提问作者Prajjwal Rajput
相关产品推荐
相关产品推荐

