如何在TanStack Table(React-Table V8)中处理一对多关系
Tanstack Table 实现分类-费用一对多可展开表格方案
Tanstack Table 完全适配这类一对多关系的表格需求,不需要额外重写核心渲染逻辑,只需要利用其内置的子行配置和展开插件就能解决你的问题。以下是具体实现步骤:
核心思路
通过getSubRows配置将每个分类对应的expenses数组作为子行数据,配合useExpanded插件实现展开/折叠功能,再通过列渲染逻辑区分父行(分类)和子行(费用)的显示内容。
具体实现代码
1. 定义数据类型
// 费用项类型 type Expense = { id: string; amount: number; date: string; description: string; }; // 分类汇总类型 type Accounting = { category: string; total: number; expenses: Expense[]; };
2. 配置表格列与展开逻辑
import { useReactTable, createColumnHelper, useExpanded } from '@tanstack/react-table'; const columnHelper = createColumnHelper<Accounting | Expense>(); const columns = [ // 展开/折叠按钮列 columnHelper.display({ id: 'expand', cell: ({ row }) => { // 仅父行(分类行)显示展开按钮 if (row.getCanExpand()) { return ( <button onClick={() => row.toggleExpanded()} style={{ border: 'none', background: 'transparent', cursor: 'pointer' }} > {row.getIsExpanded() ? '▼' : '▶'} </button> ); } return null; }, }), // 分类列:仅父行显示 columnHelper.accessor('category', { header: '分类', cell: ({ row }) => { return 'category' in row.original ? row.original.category : ''; }, }), // 总计列:仅父行显示 columnHelper.accessor('total', { header: '总计', cell: ({ row }) => { return 'total' in row.original ? `¥${row.original.total.toFixed(2)}` : ''; }, }), // 费用金额列:仅子行显示 columnHelper.accessor('amount', { header: '费用金额', cell: ({ row }) => { return 'amount' in row.original ? `¥${row.original.amount.toFixed(2)}` : ''; }, }), // 日期列:仅子行显示 columnHelper.accessor('date', { header: '日期', cell: ({ row }) => { return 'date' in row.original ? row.original.date : ''; }, }), // 描述列:仅子行显示 columnHelper.accessor('description', { header: '描述', cell: ({ row }) => { return 'description' in row.original ? row.original.description : ''; }, }), ];
3. 初始化表格实例
const YourTableComponent = ({ accountingData }: { accountingData: Accounting[] }) => { const table = useReactTable({ data: accountingData, columns, // 指定子行数据:每个分类的expenses数组作为子行 getSubRows: (row) => { if ('expenses' in row.original) { return row.original.expenses; } return []; }, // 启用展开插件 plugins: [useExpanded], }); // 渲染表格 return ( <table style={{ borderCollapse: 'collapse', width: '100%' }}> <thead> {table.getHeaderGroups().map(headerGroup => ( <tr key={headerGroup.id}> {headerGroup.headers.map(header => ( <th key={header.id} style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left' }} > {header.renderHeader()} </th> ))} </tr> ))} </thead> <tbody> {table.getRowModel().rows.map(row => ( <React.Fragment key={row.id}> <tr style={{ border: '1px solid #ddd' }}> {row.getVisibleCells().map(cell => ( <td key={cell.id} style={{ padding: '8px', border: '1px solid #ddd', // 子行添加缩进,视觉区分 paddingLeft: 'category' in row.original ? '8px' : '24px' }} > {cell.renderCell()} </td> ))} </tr> </React.Fragment> ))} </tbody> </table> ); };
关键说明
getSubRows是核心配置:它告诉Tanstack Table每个父行对应的子行数据,直接返回分类的expenses数组即可。- 列渲染区分父/子行:通过判断原始数据的属性(如
'category' in row.original)来决定当前列是否显示内容,避免子行出现父行的字段。 - 样式优化:给子行添加缩进可以让层级关系更清晰,提升用户体验。
内容的提问来源于stack exchange,提问作者Zenor27
相关产品推荐
相关产品推荐

