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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 19:40:20