🥥 前沿
作为一名前端,可能对"表格"这个 UI 组件并不陌生,市面上大多数组件库也都会提供表格组件,但说到 @tanstack/react-table 这个库时,脑海里可能冒出三连问:
🌚 它是谁? 🧠 我该怎么开始用?
别急!今天我们就通过官方示例代码来拆解一下 @tanstack/react-table 的基本用法!
🔔 对官方示例代码可能存在一些删减的情况
代码地址🔗: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.accessor('firstName', {
cell: (info) => info.getValue(),
footer: (info) => info.column.id,
}),
columnHelper.accessor((row) => row.lastName, {
id: 'lastName',
cell: (info) => <i>{info.getValue()}</i>,
header: () => <span>Last Name</span>,
footer: (info) => info.column.id,
}),
columnHelper.accessor('age', {
header: () => 'Age',
cell: (info) => info.renderValue(),
footer: (info) => info.column.id,
}),
columnHelper.accessor('visits', {
header: () => <span>Visits</span>,
footer: (info) => info.column.id,
}),
columnHelper.accessor('status', {
header: 'Status',
footer: (info) => info.column.id,
}),
columnHelper.accessor('progress', {
header: 'Profile Progress',
footer: (info) => info.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}>
{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}>
{flexRender(
header.column.columnDef.footer,
header.getContext()
)}
</th>
))}
</tr>
))}
</tfoot>
</table>
</div>
);
}
🧱 结构速览
tsx
type Person = { ... } // 数据类型定义
const defaultData: Person[] = [ ... ] // 原始数据
const columnHelper = createColumnHelper<Person>(); // 创建列辅助器
const columns = [ ... ] // 定义表格的列
const table = useReactTable({
data: defaultData,
columns,
getCoreRowModel: getCoreRowModel(),
});
return (
<table>
<thead>...</thead>
<tbody>...</tbody>
<tfoot>...</tfoot>
</table>
);
看过去觉得,好像写了挺多东西?没关系,我们来一个个解构。
🔍 第一步:定义数据类型 & 数据源
TypeScript 类型定义,让表格知道数据长什么样
tsx
type Person = {
firstName: string;
lastName: string;
age: number;
visits: number;
status: string;
progress: number;
};
定义一些假数据,准备拿来展示用
tsx
const defaultData: Person[] = [
{ firstName: 'tanner', lastName: 'linsley', age: 24, ... },
...
];
🧰 第二步:定义列(columns)
这里用到了一个 API:createColumnHelper<Person>()
API链接🔗:tanstack.com/table/lates...
🧩 一句话简介: createColumnHelper<T>()
是一个 "列构造器",它帮助你更方便地定义列(columns
)的时候自动带上类型,少写代码,少出错。
tsx
const columns = [
columnHelper.accessor('firstName', {
cell: (info) => info.getValue(), // 如何显示单元格
footer: (info) => info.column.id, // 如何显示页脚
}),
...
];
特别注意:有些列是通过 accessor
函数自定义的,比如:
tsx
columnHelper.accessor((row) => row.lastName, {
id: 'lastName',
...
})
这样可以灵活处理你希望"用函数方式"生成的列值。
⚙️ 第三步:创建表格对象
tsx
const table = useReactTable({
data: defaultData,
columns,
getCoreRowModel: getCoreRowModel(), // 获取核心行模型
});
这一步是表格的"大脑"。它生成一个 table
对象,这个对象里包含了所有你需要渲染的东西:头部、行、单元格、页脚等。
🧱 第四步:渲染表格
表格渲染(也就是把 table
对象渲染成 HTML
的部分)涉及一些不熟悉的新 API
-
table.getHeaderGroups()
-
header.isPlaceholder
-
header.getContext()
-
row.getVisibleCells()
-
cell.getContext()
-
flexRender(...)
表格渲染结构总览 这是你现在看到的代码:
tsx
<table>
<thead>
{table.getHeaderGroups().map(...)}
</thead>
<tbody>
{table.getRowModel().rows.map(...)}
</tbody>
<tfoot>
{table.getFooterGroups().map(...)}
</tfoot>
</table>
它其实就是三个部分:
-
<thead>
渲染表头 -
<tbody>
渲染每一行数据 -
<tfoot>
渲染页脚
<thead>
:渲染页头
tsx
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
🔍 逐行解析:
table.getHeaderGroups()
→ 获取的是"分组表头",支持多层嵌套列。返回一个数组,每一项对应 <tr>
。
headerGroup.headers
→ 每个 headerGroup
包含一个 headers
数组,每项对应 <th>
。
flexRender(...)
→ 表格组件的万能"渲染器":不管你写的是 JSX 还是字符串,都会统一渲染出来。
header.getContext()
→ 给 flexRender()
提供上下文数据(比如列信息、排序状态等)。
<tbody>
:渲染数据行
tsx
<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>
🔍 逐行解析:
table.getRowModel().rows
→ 获取所有可见的行数据。
row.getVisibleCells()
→ 每一行对应哪些单元格(即哪些列)。
cell.column.columnDef.cell
→ 当前 cell(单元格)应该如何渲染,取决于你在 columns 中定义的 cell 属性。
cell.getContext()
→ 提供当前单元格的上下文环境(比如 row data 等)。
flexRender(...)
→ 再次登场!万能渲染函数。它负责把你写的 JSX、函数、字符串等都正确渲染成 HTML。
<tfoot>
:渲染页脚
tsx
<tfoot>
{table.getFooterGroups().map((footerGroup) => (
<tr key={footerGroup.id}>
{footerGroup.headers.map((header) => (
<th key={header.id}>
{flexRender(header.column.columnDef.footer, header.getContext())}
</th>
))}
</tr>
))}
</tfoot>
页脚的结构和表头几乎一样,只不过渲染的是你在 columns
中写的 footer
属性,比如:
footer: (info) => info.column.id
🎉 最终效果
你将会看到一个整齐的原始表格,包括头部、数据行、页脚。虽然外观看起来比较"朴素",但结构非常灵活,为后续拓展分页、排序、筛选等功能打下基础。
