TanStackTable技能 tanstack-table

本技能提供构建生产就绪的无头数据表格,使用 TanStack Table v8,优化服务器端模式和 Cloudflare Workers 集成。支持分页、过滤、排序、虚拟化等功能,并包含常见错误预防和最佳实践,适用于前端开发、数据可视化、表格状态管理、TanStack Query 协调、Cloudflare D1 数据库集成、大型数据集性能优化。

前端开发 0 次安装 0 次浏览 更新于 3/7/2026

名称: tanstack-table 描述: TanStack Table v8 无头数据表格,具有服务器端功能,用于 Cloudflare Workers + D1。用于分页、过滤、排序、虚拟化,或遇到状态管理、TanStack Query 协调、URL 同步错误。 许可证: MIT 允许工具: [Bash, Read, Write, Edit] 元数据: 版本: 1.1.0 作者: Claude Skills Maintainers 最后验证: 2025-12-09 生产测试: true 关键词: - tanstack table - react table - 数据表格 - 数据网格 - 无头表格 - 服务器端分页 - 服务器端过滤 - 服务器端排序 - tanstack query 集成 - cloudflare d1 - cloudflare workers - 虚拟化 - tanstack virtual - 大型数据集 - 表格状态管理 - url 状态同步 - 列配置 - typescript 表格 - react table v8 - 无头 ui - 数据可视化 - 分页 - 过滤 - 排序

TanStack Table 技能

构建生产就绪的无头数据表格,使用 TanStack Table v8,针对服务器端模式和 Cloudflare Workers 集成进行优化。


何时使用此技能

自动触发当您提到:

  • “数据表格” 或 “数据网格”
  • “服务器端分页” 或 “服务器端过滤”
  • “TanStack Table” 或 “React Table”
  • “具有大型数据集的表格”
  • “通过 API 分页/过滤/排序”
  • “Cloudflare D1 表格集成”
  • “虚拟化表格” 或 “大型列表性能”

使用此技能当:

  • 构建具有分页、过滤或排序的数据表格
  • 实现服务器端表格功能(API 驱动)
  • 与 TanStack Query 集成进行数据获取
  • 处理需要虚拟化的大型数据集(1000+ 行)
  • 连接表格到 Cloudflare D1 数据库
  • 需要无头表格逻辑而不依赖特定 UI
  • 从其他表格库迁移到 TanStack Table v8

此技能提供的内容

1. 生产模板(7个)

  • 基本客户端表格 - 简单表格,使用本地数据
  • 服务器分页表格 - API 驱动的分页,与 TanStack Query 集成
  • D1 数据库集成 - Cloudflare D1 + Workers API + 表格
  • 列配置模式 - 类型安全的列定义
  • 受控表格状态 - 列可见性、固定、排序、模糊/全局过滤、行选择
  • 虚拟化大型数据集 - 使用 TanStack Virtual 进行性能优化
  • shadcn/ui 样式表格 - 与 Tailwind v4 + shadcn 集成

2. 服务器端模式

  • 与 API 后端的分页
  • 使用查询参数的过滤
  • 与数据库查询的排序
  • 状态管理(页面、过滤器、排序)
  • URL 同步
  • TanStack Query 协调

3. Cloudflare 集成

  • D1 数据库查询模式
  • Workers API 端点用于表格数据
  • SQL 中的分页 + 过滤 + 排序
  • 绑定设置(wrangler.jsonc)
  • 客户端集成模式

4. 性能优化

  • 使用 TanStack Virtual 进行虚拟化
  • 大型数据集渲染(10k+ 行)
  • 内存高效模式
  • useVirtualizer() 集成

5. 功能控制和用户体验

  • 列可见性切换和固定(冻结列)
  • 列排序和大小默认值
  • 全局 + 模糊搜索和分面过滤器
  • 行选择和行固定模式
  • 受控状态检查清单以避免性能退化

6. 错误预防

文档和预防 6+ 常见问题:

  1. 服务器端状态管理混淆
  2. TanStack Query 集成错误(查询键协调)
  3. 使用 API 后端的列过滤
  4. 手动排序设置错误
  5. URL 状态同步问题
  6. 大型数据集性能问题
  7. 过度控制表格状态(columnSizingInfo)导致额外渲染

快速开始

安装

# 核心表格库
bun add @tanstack/react-table@latest

# 可选:用于虚拟化(1000+ 行)
bun add @tanstack/react-virtual@latest

# 可选:用于模糊/全局搜索
bun add @tanstack/match-sorter-utils@latest

最新验证版本(截至 2025-12-09):

  • @tanstack/react-table: v8.21.3(稳定)
  • @tanstack/react-virtual: v3.13.12
  • @tanstack/match-sorter-utils: v8.21.3(用于模糊过滤)

React 支持: 适用于 React 16.8+ 到 React 19;不支持 React Compiler。

基本客户端表格

import { useReactTable, getCoreRowModel, ColumnDef } from '@tanstack/react-table'
import { useMemo } from 'react'

interface User {
  id: string
  name: string
  email: string
}

const columns: ColumnDef<User>[] = [
  { accessorKey: 'id', header: 'ID' },
  { accessorKey: 'name', header: '名称' },
  { accessorKey: 'email', header: '邮箱' },
]

function UsersTable() {
  // 关键:使用 useMemo 记忆数据和列,防止无限重新渲染
  const data = useMemo<User[]>(() => [
    { id: '1', name: 'Alice', email: 'alice@example.com' },
    { id: '2', name: 'Bob', email: 'bob@example.com' },
  ], [])

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(), // 必需
  })

  return (
    <table>
      <thead>
        {table.getHeaderGroups().map(headerGroup => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map(header => (
              <th key={header.id}>
                {header.isPlaceholder ? null : header.column.columnDef.header}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map(row => (
          <tr key={row.id}>
            {row.getVisibleCells().map(cell => (
              <td key={cell.id}>
                {cell.renderValue()}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  )
}

服务器端模式(推荐用于大型数据集)

模式 1:使用 TanStack Query 的服务器端分页

Cloudflare Workers API 端点:

// src/routes/api/users.ts
import { Env } from '../../types'

export async function onRequestGet(context: { request: Request; env: Env }) {
  const url = new URL(context.request.url)
  const page = Number(url.searchParams.get('page')) || 0
  const pageSize = Number(url.searchParams.get('pageSize')) || 20

  const offset = page * pageSize

  // 查询 D1 数据库
  const { results, meta } = await context.env.DB.prepare(`
    SELECT id, name, email, created_at
    FROM users
    ORDER BY created_at DESC
    LIMIT ? OFFSET ?
  `).bind(pageSize, offset).all()

  // 获取总计数用于分页
  const countResult = await context.env.DB.prepare(`
    SELECT COUNT(*) as total FROM users
  `).first<{ total: number }>()

  return Response.json({
    data: results,
    pagination: {
      page,
      pageSize,
      total: countResult?.total || 0,
      pageCount: Math.ceil((countResult?.total || 0) / pageSize),
    },
  })
}

使用 TanStack Query 的客户端表格:

import { useReactTable, getCoreRowModel, PaginationState } from '@tanstack/react-table'
import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'

function ServerPaginatedTable() {
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: 20,
  })

  // TanStack Query 获取数据
  const { data, isLoading } = useQuery({
    queryKey: ['users', pagination.pageIndex, pagination.pageSize],
    queryFn: async () => {
      const response = await fetch(
        `/api/users?page=${pagination.pageIndex}&pageSize=${pagination.pageSize}`
      )
      return response.json()
    },
  })

  // TanStack Table 管理显示
  const table = useReactTable({
    data: data?.data ?? [],
    columns,
    getCoreRowModel: getCoreRowModel(),
    // 服务器端分页配置
    manualPagination: true, // 关键:告诉表格分页是手动的
    pageCount: data?.pagination.pageCount ?? 0,
    state: { pagination },
    onPaginationChange: setPagination,
  })

  if (isLoading) return <div>加载中...</div>

  return (
    <div>
      <table>{/* 渲染表格 */}</table>

      {/* 分页控件 */}
      <div>
        <button
          onClick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
        >
          上一页
        </button>
        <span>
          第 {table.getState().pagination.pageIndex + 1} 页,共 {table.getPageCount()} 页
        </span>
        <button
          onClick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
        >
          下一页
        </button>
      </div>
    </div>
  )
}

模式 2:服务器端过滤

支持过滤的 API:

export async function onRequestGet(context: { request: Request; env: Env }) {
  const url = new URL(context.request.url)
  const search = url.searchParams.get('search') || ''

  const { results } = await context.env.DB.prepare(`
    SELECT * FROM users
    WHERE name LIKE ? OR email LIKE ?
    LIMIT 20
  `).bind(`%${search}%`, `%${search}%`).all()

  return Response.json({ data: results })
}

客户端:

const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])

const { data } = useQuery({
  queryKey: ['users', columnFilters],
  queryFn: async () => {
    const search = columnFilters.find(f => f.id === 'search')?.value || ''
    return fetch(`/api/users?search=${search}`).then(r => r.json())
  },
})

const table = useReactTable({
  data: data?.data ?? [],
  columns,
  getCoreRowModel: getCoreRowModel(),
  manualFiltering: true, // 关键:服务器处理过滤
  state: { columnFilters },
  onColumnFiltersChange: setColumnFilters,
})

大型数据集的虚拟化

对于 1000+ 行,使用 TanStack Virtual 仅渲染可见行:

import { useVirtualizer } from '@tanstack/react-virtual'
import { useRef } from 'react'

function VirtualizedTable() {
  const tableContainerRef = useRef<HTMLDivElement>(null)

  const table = useReactTable({
    data: largeDataset, // 10k+ 行
    columns,
    getCoreRowModel: getCoreRowModel(),
  })

  const { rows } = table.getRowModel()

  // 虚拟化行
  const rowVirtualizer = useVirtualizer({
    count: rows.length,
    getScrollElement: () => tableContainerRef.current,
    estimateSize: () => 50, // 行高(像素)
    overscan: 10, // 渲染额外 10 行以确保滚动平滑
  })

  return (
    <div ref={tableContainerRef} style={{ height: '600px', overflow: 'auto' }}>
      <table style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
        <thead>{/* 表头 */}</thead>
        <tbody>
          {rowVirtualizer.getVirtualItems().map(virtualRow => {
            const row = rows[virtualRow.index]
            return (
              <tr
                key={row.id}
                style={{
                  position: 'absolute',
                  transform: `translateY(${virtualRow.start}px)`,
                  width: '100%',
                }}
              >
                {row.getVisibleCells().map(cell => (
                  <td key={cell.id}>{cell.renderValue()}</td>
                ))}
              </tr>
            )
          })}
        </tbody>
      </table>
    </div>
  )
}

常见错误与解决方案

错误 1:无限重新渲染

问题: 表格无限重新渲染,浏览器冻结。

原因: datacolumns 引用在每次渲染时变化。

解决方案: 始终使用 useMemouseState

// ❌ 错误:每次渲染创建新数组引用
function Table() {
  const data = [{ id: 1 }] // 创建新数组!
  const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
}

// ✅ 正确:稳定引用
function Table() {
  const data = useMemo(() => [{ id: 1 }], []) // 稳定
  const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
}

// ✅ 也正确:在组件外定义
const data = [{ id: 1 }]
function Table() {
  const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
}

错误 2:TanStack Query + 表格状态不匹配

问题: 查询重新获取数据,但分页状态不同步,导致陈旧数据。

解决方案: 在查询键中包含所有表格状态:

// ❌ 错误:查询键中缺少分页
const { data } = useQuery({
  queryKey: ['users'], // 不包含页面!
  queryFn: () => fetch(`/api/users?page=${pagination.pageIndex}`).then(r => r.json())
})

// ✅ 正确:完整查询键
const { data } = useQuery({
  queryKey: ['users', pagination.pageIndex, pagination.pageSize, columnFilters, sorting],
  queryFn: () => {
    const params = new URLSearchParams({
      page: pagination.pageIndex.toString(),
      pageSize: pagination.pageSize.toString(),
      // ... 过滤器、排序
    })
    return fetch(`/api/users?${params}`).then(r => r.json())
  }
})

错误 3:服务器端功能不工作

问题: 分页/过滤/排序不触发 API 调用。

解决方案:manual* 标志设置为 true

const table = useReactTable({
  data,
  columns,
  getCoreRowModel: getCoreRowModel(),
  // 关键:告诉表格这些是服务器端的
  manualPagination: true,
  manualFiltering: true,
  manualSorting: true,
  pageCount: serverPageCount, // 必须提供总页数
})

错误 4:TypeScript “找不到模块” 列辅助

问题: createColumnHelper 的导入错误。

解决方案: 从正确路径导入:

// ❌ 错误:错误路径
import { createColumnHelper } from '@tanstack/table-core'

// ✅ 正确:正确路径
import { createColumnHelper } from '@tanstack/react-table'

// 用法:类型安全的列
const columnHelper = createColumnHelper<User>()
const columns = [
  columnHelper.accessor('name', {
    header: '名称',
    cell: info => info.getValue(), // 完全类型化!
  }),
]

错误 5:排序不适用于服务器端

问题: 点击排序表头不更新数据。

解决方案: 在查询键和 API 调用中包含排序:

const [sorting, setSorting] = useState<SortingState>([])

const { data } = useQuery({
  queryKey: ['users', pagination, sorting], // 包含排序
  queryFn: async () => {
    const sortParam = sorting[0]
      ? `&sortBy=${sorting[0].id}&sortOrder=${sorting[0].desc ? 'desc' : 'asc'}`
      : ''
    return fetch(`/api/users?page=${pagination.pageIndex}${sortParam}`).then(r => r.json())
  }
})

const table = useReactTable({
  data: data?.data ?? [],
  columns,
  getCoreRowModel: getCoreRowModel(),
  manualSorting: true,
  state: { sorting },
  onSortingChange: setSorting,
})

错误 6:大型数据集性能差

问题: 表格在 1000+ 行时缓慢/卡顿。

解决方案: 使用虚拟化(见上方示例)或实现服务器端分页。


与现有技能集成

与 tanstack-query 技能

TanStack Table + TanStack Query 是推荐模式:

// Query 处理数据获取 + 缓存
const { data, isLoading } = useQuery({
  queryKey: ['users', tableState],
  queryFn: fetchUsers,
})

// Table 处理显示 + 交互
const table = useReactTable({
  data: data?.data ?? [],
  columns,
  getCoreRowModel: getCoreRowModel(),
})

与 cloudflare-d1 技能

// Cloudflare Workers API(来自 cloudflare-d1 技能模式)
export async function onRequestGet({ env }: { env: Env }) {
  const { results } = await env.DB.prepare('SELECT * FROM users LIMIT 20').all()
  return Response.json({ data: results })
}

// 客户端表格使用 D1 数据
const { data } = useQuery({
  queryKey: ['users'],
  queryFn: () => fetch('/api/users').then(r => r.json())
})

与 tailwind-v4-shadcn 技能

使用 shadcn/ui 表格组件与 TanStack Table 逻辑:

import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from '@/components/ui/table'

function StyledTable() {
  const table = useReactTable({ /* 配置 */ })

  return (
    <Table>
      <TableHeader>
        {table.getHeaderGroups().map(headerGroup => (
          <TableRow key={headerGroup.id}>
            {headerGroup.headers.map(header => (
              <TableHead key={header.id}>
                {header.column.columnDef.header}
              </TableHead>
            ))}
          </TableRow>
        ))}
      </TableHeader>
      <TableBody>
        {table.getRowModel().rows.map(row => (
          <TableRow key={row.id}>
            {row.getVisibleCells().map(cell => (
              <TableCell key={cell.id}>
                {cell.renderValue()}
              </TableCell>
            ))}
          </TableRow>
        ))}
      </TableBody>
    </Table>
  )
}

最佳实践

1. 始终记忆数据和列

const data = useMemo(() => [...], [依赖项])
const columns = useMemo(() => [...], [])

2. 为大型数据集使用服务器端

  • 客户端:<1000 行
  • 服务器端:1000+ 行或频繁变化的数据

3. 协调查询键与表格状态

queryKey: ['资源', 分页, 过滤器, 排序]

4. 提供加载状态

if (isLoading) return <表格骨架 />
if (error) return <错误消息 error={error} />

5. 使用列辅助进行类型安全

const columnHelper = createColumnHelper<您的类型>()
const columns = [
  columnHelper.accessor('字段', { /* 完全类型化 */ })
]

6. 虚拟化大型客户端表格

if (data.length > 1000) {
  // 使用 TanStack Virtual(见上方示例)
}

7. 仅控制所需状态

  • 在需要持久化或同步时,保持 排序分页过滤器可见性固定排序选择 在受控状态。
  • 除非持久化拖动状态,避免控制 columnSizingInfo;它会触发频繁更新并可能影响性能。

模板参考

所有模板在 ~/.claude/skills/tanstack-table/templates/ 中可用:

  1. package.json - 依赖和版本
  2. basic-client-table.tsx - 简单客户端表格
  3. server-paginated-table.tsx - 使用 Query 的服务器端分页
  4. d1-database-example.tsx - Cloudflare D1 集成
  5. column-configuration.tsx - 类型安全的列模式
  6. controlled-table-state.tsx - 可见性、固定、排序、模糊/全局过滤、选择
  7. virtualized-large-dataset.tsx - 使用 Virtual 的性能
  8. shadcn-styled-table.tsx - Tailwind v4 + shadcn UI 样式

参考文档

深入指南在 ~/.claude/skills/tanstack-table/references/

  1. server-side-patterns.md - 使用 API 的分页、过滤、排序
  2. query-integration.md - 与 TanStack Query 协调
  3. cloudflare-d1-examples.md - Workers + D1 完整示例
  4. performance-virtualization.md - TanStack Virtual 指南
  5. common-errors.md - 所有 6+ 文档化问题与解决方案
  6. feature-controls.md - 受控状态、可见性、固定、排序、模糊/全局过滤、选择

何时加载参考

Claude 应根据用户需求建议加载这些参考文件:

加载 references/common-errors.md 当:

  • 用户遇到无限重新渲染或表格冻结
  • 查询数据不与分页状态变化同步
  • 服务器端功能(分页/过滤/排序)不触发 API 调用
  • TypeScript 错误与列辅助导入
  • 排序状态变化不更新 API 调用
  • 1000+ 行客户端性能问题
  • 任何在 6 个文档化问题中提到的错误消息

加载 references/server-side-patterns.md 当:

  • 用户询问实现与 API 后端的分页
  • 需要构建带后端查询参数的过滤
  • 实现与数据库查询的排序
  • 构建 Cloudflare Workers 或任何用于表格数据的 API 端点
  • 协调表格状态(页面、过滤器、排序)与服务器调用
  • 关于 manualPagination、manualFiltering 或 manualSorting 标志的问题

加载 references/query-integration.md 当:

  • 协调 TanStack Table + TanStack Query 一起使用
  • 查询键和表格状态同步问题
  • 当分页/过滤/排序变化时的重新获取模式
  • 带表格状态的查询键组合
  • 服务器端表格的陈旧数据问题

加载 references/cloudflare-d1-examples.md 当:

  • 构建 Cloudflare Workers API 端点用于表格数据
  • 编写带分页/过滤的 D1 数据库查询
  • 需要完整的端到端 Cloudflare 集成示例
  • 表格功能的 SQL 查询模式(LIMIT/OFFSET、WHERE、ORDER BY)
  • D1 + 表格的 wrangler.jsonc 绑定设置

加载 references/performance-virtualization.md 当:

  • 处理 1000+ 行数据集客户端
  • TanStack Virtual 集成问题
  • 内存高效渲染模式
  • useVirtualizer() 钩子使用
  • 大型表格性能优化
  • 关于行虚拟化或滚动性能的问题

加载 references/feature-controls.md 当:

  • 需要列可见性、固定或排序控制
  • 构建工具栏(全局搜索、切换)或将状态同步到 URL/本地存储
  • 实现模糊/全局搜索或分面过滤器
  • 设置行选择/固定或受控分页/排序

令牌效率

无此技能:

  • ~8,000 令牌:研究 v8 变化、服务器端模式、Query 集成
  • 3-4 个常见错误遇到
  • 30-45 分钟总时间

有此技能:

  • ~3,500 令牌:直接模板、错误预防
  • 0 错误(所有文档化问题预防)
  • 10-15 分钟总时间

节省: ~55-65% 令牌,~70% 时间


生产验证

测试使用:

  • React 19.2
  • Vite 6.0
  • TypeScript 5.8
  • Cloudflare Workers(Wrangler 4.0)
  • TanStack Query v5.90.7(tanstack-query 技能)
  • Tailwind v4 + shadcn/ui(tailwind-v4-shadcn 技能)

堆栈兼容性:

  • ✅ Cloudflare Workers + 静态资产
  • ✅ Cloudflare D1 数据库
  • ✅ TanStack Query 集成
  • ✅ React 19.2+ 服务器组件
  • ✅ TypeScript 严格模式
  • ✅ Vite 6.0+ 构建优化

进一步阅读


最后更新: 2025-12-09 技能版本: 1.1.0 库版本: @tanstack/react-table v8.21.3