跟着官方示例学习 @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>
相关推荐
Hilaku8 分钟前
我用AI重构了一段500行的屎山代码,这是我的Prompt和思考过程
前端·javascript·架构
Cxiaomu9 分钟前
React Native App 自动检测版本更新完整实现指南
javascript·react native·react.js
IT_陈寒34 分钟前
Vite性能优化实战:5个被低估的配置让你的开发效率提升50%
前端·人工智能·后端
IT_陈寒36 分钟前
Java性能调优的7个被低估的技巧:从代码到JVM全链路优化
前端·人工智能·后端
掘金安东尼1 小时前
前端周刊第439期(2025年11月3日–11月9日)
前端·javascript·vue.js
码农刚子1 小时前
ASP.NET Core Blazor进阶1:高级组件开发
前端·前端框架
道可到1 小时前
重新审视 JavaScript 中的异步循环
前端
起这个名字2 小时前
微前端应用通信使用和原理
前端·javascript·vue.js
QuantumLeap丶2 小时前
《uni-app跨平台开发完全指南》- 06 - 页面路由与导航
前端·vue.js·uni-app
CSharp精选营2 小时前
ASP.NET Core Blazor进阶1:高级组件开发
前端·.net core·blazor