跟着官方示例学习 @tanStack-table --- Header Groups

🌲 系列一:跟着官方示例学习 @tanStack-table --- Basic


在上一篇中,我们已经愉快地搭好了一个基本的表格。这次我们来给表格加点"分组逻辑",让表头也学会团队合作,一起登场!

没错,我们今天的主题是:🧩 Header Groups(表头分组)

✨ 为什么需要表头分组?

当你有很多列,或者希望某几列逻辑上属于同一类时,"分组表头"就变得超级有用了。

比如我们希望把 firstNamelastName 归为一组叫 Name,把 agevisitsstatusprogress 归为另一组叫 Info,那就可以用 分组列配置 来实现。

🔔 对官方示例代码可能存在一些删减的情况

代码链接🔗:tanstack.com/table/lates...

tsx 复制代码
type Person = {
  firstName: string
  lastName: string
  age: number
  visits: number
  status: string
  progress: number
}

const defaultData: Person[] = [
  {
    firstName: 'tanner',
    lastName: 'linsley',
    age: 24,
    visits: 100,
    status: 'In Relationship',
    progress: 50,
  },
  {
    firstName: 'tandy',
    lastName: 'miller',
    age: 40,
    visits: 40,
    status: 'Single',
    progress: 80,
  },
  {
    firstName: 'joe',
    lastName: 'dirte',
    age: 45,
    visits: 20,
    status: 'Complicated',
    progress: 10,
  },
]

const columnHelper = createColumnHelper<Person>()

const columns = [
  columnHelper.group({
    id: 'name',
    header: () => <span>Name</span>,
    columns: [
      columnHelper.accessor('firstName', {
        cell: info => info.getValue(),
        footer: props => props.column.id,
      }),
      columnHelper.accessor(row => row.lastName, {
        id: 'lastName',
        cell: info => info.getValue(),
        header: () => <span>Last Name</span>,
        footer: props => props.column.id,
      }),
    ],
  }),
  columnHelper.group({
    header: 'Info',
    footer: props => props.column.id,
    columns: [
      columnHelper.accessor('age', {
        header: () => 'Age',
        footer: props => props.column.id,
      }),
      columnHelper.group({
        header: 'More Info',
        columns: [
          columnHelper.accessor('visits', {
            header: () => <span>Visits</span>,
            footer: props => props.column.id,
          }),
          columnHelper.accessor('status', {
            header: 'Status',
            footer: props => props.column.id,
          }),
          columnHelper.accessor('progress', {
            header: 'Profile Progress',
            footer: props => props.column.id,
          }),
        ],
      }),
    ],
  }),
]

function App() {
  const table = useReactTable({
    data: defaultData,
    columns,
    getCoreRowModel: getCoreRowModel(),
  })

  return (
    <div className="p-2">
      <table>
        <thead>
          {table.getHeaderGroups().map(headerGroup => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map(header => (
                <th key={header.id} colSpan={header.colSpan}>
                  {flexRender(header.column.columnDef.header, header.getContext())}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map(row => (
            <tr key={row.id}>
              {row.getVisibleCells().map(cell => (
                <td key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
        <tfoot>
          {table.getFooterGroups().map(footerGroup => (
            <tr key={footerGroup.id}>
              {footerGroup.headers.map(header => (
                <th key={header.id} colSpan={header.colSpan}>
                  {flexRender(header.column.columnDef.footer, header.getContext())}
                </th>
              ))}
            </tr>
          ))}
        </tfoot>
      </table>
    </div>
  )
}

🛠 怎么做?

我们不再直接用 accessor 一列一列堆叠了,而是用 columnHelper.group() 创建分组:

tsx 复制代码
const columns = [
  columnHelper.group({
    id: 'hello',
    header: () => <span>Hello</span>,
    columns: [
      columnHelper.accessor('firstName', { ... }),
      columnHelper.accessor(row => row.lastName, { ... }),
    ],
  }),
  columnHelper.group({
    header: 'Info',
    columns: [
      columnHelper.accessor('age', { ... }),
      columnHelper.group({
        header: 'More Info',
        columns: [
          columnHelper.accessor('visits', { ... }),
          columnHelper.accessor('status', { ... }),
          columnHelper.accessor('progress', { ... }),
        ],
      }),
    ],
  }),
]

是的,你没有看错,分组是可以嵌套的,像"套娃"一样!👶🧒👨

🧠 group() 的几个关键点

参数 说明
id 分组的唯一标识(不是必须的,但加了会更清晰)
header 分组的表头内容,可以是字符串或函数返回 React 元素
columns 这个分组下的子列或子分组,数组形式

每个 group()accessor() 都是"列对象",可以嵌套组成一棵列树 🌲。

📦 渲染时发生了什么?

我们之前在 <thead> 里遍历的是:

tsx 复制代码
table.getHeaderGroups().map((headerGroup) => (
  <tr>
    {headerGroup.headers.map((header) => (
      <th colSpan={header.colSpan}>
        {flexRender(...)}
      </th>
    ))}
  </tr>
))

注意两点:

getHeaderGroups() 会自动根据你的嵌套分组列生成多层表头;

每个 header 带有 colSpan 属性,表示它横跨几列 ------ 必须用于 <th colSpan={...}> 才能对齐!

📊 最终效果是?

你可以把它理解为一个二维表头。更清晰、分组明确,是不是感觉更"专业表格"了!

但是看这个表格,有些表头是为了布局而存在的"占位单元格"(placeholder cells),它们不包含内容,就是为了撑开结构用的。

header 设置了 isPlaceholder: true,你就可以根据它是否是"空单元格"来决定 要不要渲染内容。

🪐 isplaceholder

tanstack.com/table/lates...

tsx 复制代码
<th key={header.id} colSpan={header.colSpan}>
  {header.isPlaceholder
    ? null
    : flexRender(
        header.column.columnDef.header,
        header.getContext()
      )}
</th>
相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax