跟着官方示例学习 @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>
相关推荐
dog shit20 分钟前
web第十次课后作业--Mybatis的增删改查
android·前端·mybatis
我有一只臭臭20 分钟前
el-tabs 切换时数据不更新的问题
前端·vue.js
七灵微24 分钟前
【前端】工具链一本通
前端
Nueuis1 小时前
微信小程序前端面经
前端·微信小程序·小程序
_r0bin_4 小时前
前端面试准备-7
开发语言·前端·javascript·fetch·跨域·class
IT瘾君4 小时前
JavaWeb:前端工程化-Vue
前端·javascript·vue.js
potender4 小时前
前端框架Vue
前端·vue.js·前端框架
站在风口的猪11084 小时前
《前端面试题:CSS预处理器(Sass、Less等)》
前端·css·html·less·css3·sass·html5
程序员的世界你不懂5 小时前
(9)-Fiddler抓包-Fiddler如何设置捕获Https会话
前端·https·fiddler
MoFe15 小时前
【.net core】天地图坐标转换为高德地图坐标(WGS84 坐标转 GCJ02 坐标)
java·前端·.netcore