TanStack Table(React-Table):如何将列筛选状态转为后端预期格式?
解决React表格筛选状态转nestjs-paginate格式的问题
这两个方案其实各有适用场景,我帮你拆解下优劣,再给你具体的代码示例:
方案1:列定义中直接绑定filterFn(适合小型项目/列少的场景)
这个方案的核心是把每列对应的后端筛选操作符直接写在列定义的filterFn属性里,前端筛选时直接调用这个函数生成后端需要的格式。优点是直观,每列的筛选逻辑和列定义绑定在一起,不用额外维护映射关系;缺点是列多的时候会有重复代码。
示例代码:
首先定义带filterFn的列:
import { ColumnDef } from '@tanstack/react-table'; // 假设你的表格数据类型是User type User = { id: number; name: string; age: number; email: string; }; const columnDefs: ColumnDef<User>[] = [ { id: 'name', header: '姓名', accessorKey: 'name', // 姓名用精确匹配$eq filterFn: (value: unknown) => ({ $eq: value }), }, { id: 'age', header: '年龄', accessorKey: 'age', // 年龄用大于$gt,同时把unknown的value转成数字 filterFn: (value: unknown) => ({ $gt: Number(value) }), }, { id: 'email', header: '邮箱', accessorKey: 'email', // 邮箱用模糊匹配$like,自动加通配符 filterFn: (value: unknown) => ({ $like: `%${value}%` }), }, ];
然后写转换函数:
import { ColumnFiltersState } from '@tanstack/react-table'; const convertToNestJsFilters = (filters: ColumnFiltersState, columns: ColumnDef<User>[]) => { const nestJsFilters: Record<string, any> = {}; filters.forEach(filter => { const column = columns.find(col => col.id === filter.id); if (column?.filterFn) { // 调用列定义里的filterFn生成对应格式 nestJsFilters[column.accessorKey || column.id] = column.filterFn(filter.value); } }); return nestJsFilters; };
使用的时候,拿到表格的columnFilters状态,调用这个函数就能得到后端需要的格式,比如:
// 假设表格的筛选状态是:[{ id: 'age', value: '20' }, { id: 'name', value: '张三' }] const converted = convertToNestJsFilters(columnFilters, columnDefs); // 转换结果:{ age: { $gt: 20 }, name: { $eq: '张三' } }
方案2:自定义列Meta+通用映射函数(适合中大型项目/列多的场景)
这个方案更具扩展性,我们给列定义加一个自定义的meta属性来指定筛选操作符,然后写一个通用的映射函数来统一转换。优点是逻辑集中,新增列只需要配置meta,不用修改转换函数;缺点是需要额外定义类型,初期要多写一点代码。
示例代码:
首先定义带Meta的列类型:
import { ColumnDef } from '@tanstack/react-table'; // 自定义Meta类型,支持nestjs-paginate的所有筛选操作符 type FilterMeta = { filterOperator: '$eq' | '$gt' | '$lt' | '$like' | '$in' | string; // 可选:如果需要特殊处理值,可以加个transform函数 valueTransform?: (value: unknown) => unknown; }; // 扩展ColumnDef,加入自定义Meta type CustomColumnDef<T> = ColumnDef<T> & { meta?: FilterMeta; }; // 表格数据类型 type User = { id: number; name: string; age: number; email: string; };
然后定义列:
const columnDefs: CustomColumnDef<User>[] = [ { id: 'name', header: '姓名', accessorKey: 'name', meta: { filterOperator: '$eq' }, }, { id: 'age', header: '年龄', accessorKey: 'age', meta: { filterOperator: '$gt', valueTransform: (val) => Number(val) // 把字符串转成数字 }, }, { id: 'email', header: '邮箱', accessorKey: 'email', meta: { filterOperator: '$like', valueTransform: (val) => `%${val}%` // 自动加模糊匹配通配符 }, }, ];
然后写通用转换函数:
import { ColumnFiltersState } from '@tanstack/react-table'; const convertToNestJsFilters = (filters: ColumnFiltersState, columns: CustomColumnDef<User>[]) => { const nestJsFilters: Record<string, Record<string, unknown>> = {}; filters.forEach(filter => { const column = columns.find(col => col.id === filter.id); const meta = column?.meta; if (!meta?.filterOperator) return; // 处理值转换,没有transform就用原始值 let filterValue = meta.valueTransform ? meta.valueTransform(filter.value) : filter.value; // 生成nestjs-paginate需要的格式 nestJsFilters[column.accessorKey || column.id] = { [meta.filterOperator]: filterValue, }; }); return nestJsFilters; };
方案选择建议
- 如果你的表格只有几列,后续不会频繁新增,方案1足够简单直接,不用额外维护类型和映射逻辑;
- 如果你的表格列很多,或者后续会不断新增列,方案2更优,因为它把筛选逻辑集中管理,新增列只需要配置meta,不用修改转换函数,扩展性更强。
最后,转换后的格式可以用qs库序列化成URL查询参数(nestjs-paginate默认从query里取filter参数):
import qs from 'qs'; const filterQuery = qs.stringify({ filter: convertedFilters }, { encodeValuesOnly: true }); // 比如得到:filter[age][$gt]=20&filter[name][$eq]=张三 // 然后拼到请求URL里即可
内容的提问来源于stack exchange,提问作者monotype
相关产品推荐
相关产品推荐

