跟着官方示例学习 @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>
相关推荐
爱上好庆祝8 分钟前
学习js的第五天
前端·css·学习·html·css3·js
C澒20 分钟前
IntelliPro 产研协作平台:基于 AI Agent 的低代码智能化配置方案设计与实现
前端·低代码·ai编程
一袋米扛几楼9830 分钟前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴43 分钟前
前端与后端的区别与联系
前端
EnCi Zheng1 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen1 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技1 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人1 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实1 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha2 小时前
三目运算符
linux·服务器·前端