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

如何通过独立文件的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 05:35:53