如何通过独立文件的Checkbox组件过滤TanStack Table?
实现Shadcn表格的多标签过滤
1. 建立标签ID与文本的映射表
你的Checkbox组件中使用的是id(如forDog),但表格数据里的标签是文本(如For Dogs),首先需要一个映射关系完成转换:
// 可放在Checkbox组件或全局常量文件中 export const tagIdToLabelMap: Record<string, medTags> = { forDog: "For Dogs", forCat: "For Cats", sx: "Sx", tx: "Tx", pain: "Pain", antibiotic: "Antibiotic", steroid: "Steroid", };
2. 在父组件中管理过滤状态
将过滤状态提升到Checkbox和Table的共同父组件,用useState存储选中的标签数组,实现跨组件状态共享:
// 父组件示例(如MedicationPage.tsx) import { useState } from "react"; import { CheckboxReactHookFormMultiple } from "./CheckboxComponent"; import { DataTable } from "./data-table"; import { medications } from "./medications"; import { tagIdToLabelMap } from "./constants"; export function MedicationPage() { // 存储选中的标签文本数组 const [activeTagFilters, setActiveTagFilters] = useState<medTags[]>([]); // 处理Checkbox提交的过滤数据 const handleTagFilterSubmit = (formData: { tagFilters: string[] }) => { // 将选中的ID转换为对应的标签文本 const convertedTags = formData.tagFilters.map(id => tagIdToLabelMap[id]); setActiveTagFilters(convertedTags); }; return ( <div className="p-4"> <CheckboxReactHookFormMultiple onSubmit={handleTagFilterSubmit} /> <DataTable data={medications} tagFilters={activeTagFilters} /> </div> ); }
3. 修改Checkbox组件,接收父组件回调
更新Checkbox组件,将内部提交逻辑替换为父组件传入的回调,实现过滤数据的传递:
// CheckboxComponent.tsx import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; // 定义组件Props,接收父组件的提交回调 type CheckboxFormProps = { onSubmit: (data: z.infer<typeof FormSchema>) => void; }; const tagFilters = [ { id: "forDog", label: "For Dogs" }, { id: "forCat", label: "For Cats" }, { id: "sx", label: "Sx" }, { id: "tx", label: "Tx" }, { id: "pain", label: "Pain" }, { id: "antibiotic", label: "Antibiotic" }, { id: "steroid", label: "Steroid" }, ]; const FormSchema = z.object({ tagFilters: z .array(z.string()) .refine((value) => value.some((item) => item), { message: "至少选择一个标签", }), }); export function CheckboxReactHookFormMultiple({ onSubmit }: CheckboxFormProps) { const form = useForm<z.infer<typeof FormSchema>>({ resolver: zodResolver(FormSchema), defaultValues: { tagFilters: [ "forDog", "forCat", "sx", "tx", "pain", "antibiotic", "steroid", ], }, }); return ( <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> <FormField control={form.control} name="tagFilters" render={() => ( <FormItem> {tagFilters.map((item) => ( <FormField key={item.id} control={form.control} name="tagFilters" render={({ field }) => ( <FormItem key={item.id} className="flex flex-row items-start space-x-3 space-y-0" > <FormControl> <Checkbox checked={field.value?.includes(item.id)} onCheckedChange={(checked) => { checked ? field.onChange([...field.value, item.id]) : field.onChange( field.value?.filter((value) => value !== item.id) ); }} /> </FormControl> <FormLabel className="text-sm font-normal"> {item.label} </FormLabel> </FormItem> )} /> ))} <FormMessage /> </FormItem> )} /> <Button type="submit">应用过滤</Button> </form> </Form> ); }
4. 在Data Table中应用标签过滤
修改data-table.tsx,接收tagFilters属性,通过TanStack Table的全局过滤逻辑实现标签匹配:
// data-table.tsx import { ColumnDef, flexRender, getCoreRowModel, getFilteredRowModel, useReactTable, } from "@tanstack/react-table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { medTags, Med } from "./medications"; type DataTableProps = { data: Med[]; tagFilters: medTags[]; }; export const columns: ColumnDef<Med>[] = [ { accessorKey: "name", header: "药物名称", }, { accessorKey: "dosage", header: "剂量", }, { accessorKey: "tags", header: "标签", cell: ({ row }) => ( <div className="flex flex-wrap gap-2"> {row.getValue("tags").map((tag: string) => ( <span key={tag} className="px-2 py-1 text-xs bg-gray-100 rounded"> {tag} </span> ))} </div> ), }, // 其他列定义... ]; export function DataTable({ data, tagFilters }: DataTableProps) { const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), // 自定义全局过滤逻辑:匹配标签 globalFilterFn: (row, _, filterValue) => { const filters = filterValue as medTags[]; if (filters.length === 0) return true; // 检查当前行的标签是否包含至少一个选中的标签 return row.original.tags.some(tag => filters.includes(tag as medTags)); }, // 将选中的标签数组作为全局过滤值传入 globalFilter: tagFilters, }); return ( <div className="mt-6"> <Table> <TableHeader> {table.getHeaderGroups().map((headerGroup) => ( <TableRow key={headerGroup.id}> {headerGroup.headers.map((header) => ( <TableHead key={header.id}> {header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )} </TableHead> ))} </TableRow> ))} </TableHeader> <TableBody> {table.getRowModel().rows.map((row) => ( <TableRow key={row.id} data-state={row.getIsSelected() && "selected"} > {row.getVisibleCells().map((cell) => ( <TableCell key={cell.id}> {flexRender(cell.column.columnDef.cell, cell.getContext())} </TableCell> ))} </TableRow> ))} </TableBody> </Table> </div> ); }
补充说明
- 若需同时支持combobox搜索和标签过滤,可将combobox的搜索值也提升到父组件,在
globalFilterFn中合并两种过滤逻辑(先匹配搜索词,再匹配标签)。 - 若偏好列级过滤而非全局过滤,可给
tags列添加filterFn属性,通过table.getColumn('tags')?.setFilterValue(tagFilters)设置过滤值,此时需在父组件中保存table实例引用或用状态控制列过滤值。
内容的提问来源于stack exchange,提问作者Saar Gold
相关产品推荐
相关产品推荐

