完成后你将能够
能构建类型安全、行标识稳定且不依赖预制 UI 的基础数据表。
Table 管逻辑,不替你设计界面
TanStack Table 是无头库:它产生列、表头、行、单元格和交互状态模型,但最终 HTML、组件、键盘行为和 CSS 仍由应用负责。先用原生 table 建立正确语义,再把同一实例接入你的设计系统;不要寻找一个不存在的 Table 组件。
列定义同时描述数据与渲染
createColumnHelper 把列约束到 Issue 类型。accessor 列应返回可排序、筛选或分组的原始值;只负责按钮或链接的 display 列不应伪造 accessor。函数 accessor 必须提供稳定 id 或字符串 header。
import {
createColumnHelper, flexRender, getCoreRowModel, useReactTable,
} from '@tanstack/react-table'
type Issue = { id: string; title: string; status: 'open' | 'closed' }
const column = createColumnHelper<Issue>()
const columns = [
column.accessor('title', { header: 'Title' }),
column.accessor('status', { header: 'Status' }),
column.display({ id: 'actions', cell: ({ row }) => <a href={`/issues/${row.original.id}`}>View</a> }),
]
export function IssueTable({ issues }: { issues: Issue[] }) {
const table = useReactTable({
data: issues, columns, getRowId: (issue) => issue.id,
getCoreRowModel: getCoreRowModel(),
})
return <table><thead>{table.getHeaderGroups().map((group) => (
<tr key={group.id}>{group.headers.map((header) => <th key={header.id}>
{header.isPlaceholder ? null : 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></table>
}行模型是一条显式处理管线
getCoreRowModel 把原始 data 转成基础行。之后只有在需要排序、筛选或分页时才加入对应行模型。用 getRowId 返回业务 id,避免列表重排后选择状态、展开状态或编辑状态跟着数组位置漂移。空数据应传稳定空数组并渲染明确空状态。
把本课变成可验证的能力
能从同一份 Issue 类型推导列值、行对象和渲染上下文,并说明 Table 为什么不输出固定组件。
为 Issue 列表增加 title、status 和 priority 三列,再添加一个没有 accessor 的“查看详情”展示列。
交换数据顺序后 row.id 仍应等于 Issue id;空数组应正常渲染表头和空状态,类型检查拒绝不存在的 accessor key。
- 把 accessor 返回对象,随后期待默认排序可以理解业务字段。
- 使用数组索引作为服务端数据的稳定行标识。