You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS服务端渲染中使用TanStack v8 cell属性报错求助

解决NextJS服务端组件中TanStack Table v8列定义的cell函数报错问题

问题原因

你遇到的错误是因为:服务端组件(你的Page组件)直接将包含cell渲染函数的columns传递给了客户端组件(TaskTable)。Next.js禁止从服务端往客户端组件直接传递函数,而cell函数属于客户端渲染逻辑(返回React元素、依赖客户端组件如Badge/Button),所以触发了这个限制。

解决方案

方案1:给列定义文件添加'use client'指令

在你的列定义文件顶部加上'use client',明确标记这个文件属于客户端代码:

'use client'

import { ColumnDef } from '@tanstack/react-table'
// 导入你的Task、TaskStatus、Badge、Button等类型/组件

export const columns: ColumnDef<Task>[] = [
    // 你的列定义代码保持不变
]

同时确保TaskTable组件也标记了'use client'(因为TanStack Table的useReactTable钩子只能在客户端组件中使用):

'use client'

import { useReactTable } from '@tanstack/react-table'

export const TaskTable = ({ columns, data }) => {
    const table = useReactTable({ columns, data })
    // 表格渲染逻辑...
}

方案2:将列定义移到客户端组件内部

如果不想单独维护列定义文件,可以直接在TaskTable组件内部定义columns,让服务端组件只传递数据:

'use client'

import { ColumnDef, useReactTable } from '@tanstack/react-table'
import { Task, TaskStatus } from './types'
import { Badge, Button } from './components'

interface TaskTableProps {
    data: Task[]
}

export const TaskTable = ({ data }: TaskTableProps) => {
    // 直接在客户端组件内定义列
    const columns: ColumnDef<Task>[] = [
        {
            accessorKey: "task_status",
            header: "Status",
            cell: props => {
                const taskStatus = props.row.getValue("task_status")
                return (
                    <Badge className={"cursor-pointer w-full"} customColor={taskStatus.color_task_status}>
                        {taskStatus.name_task_status}
                    </Badge>
                )
            }
        },
        // 其他列定义...
    ]

    const table = useReactTable({ columns, data })

    // 渲染表格的代码...
}

然后修改服务端的Page组件,只传递data即可:

const Page: NextPage = async () => {
    const tasks = await getTasks()
    return (
        <div className={"w-full max-w-full flex flex-col justify-center items-center"}>
            <DarkModeToggle/>
            <h1>Tasks</h1>
             <div className={"max-w-[90vw]"}>
                <TaskTable data={tasks}/> {/* 不再传columns */}
            </div>
        </div>
    );
}

方案3:将cell逻辑抽为独立客户端组件

如果需要复用cell逻辑,可以把每个cell的渲染组件单独抽成'use client'标记的文件,然后在列定义中引用:

// StatusCell.tsx - 客户端组件
'use client'

import { CellContext } from '@tanstack/react-table'
import { Task, TaskStatus } from './types'
import { Badge } from './components'

export const StatusCell = (props: CellContext<Task, TaskStatus>) => {
    const taskStatus = props.row.getValue("task_status")
    return (
        <Badge className={"cursor-pointer w-full"} customColor={taskStatus.color_task_status}>
            {taskStatus.name_task_status}
        </Badge>
    )
}

然后在列定义文件(需加'use client')中使用:

'use client'

import { ColumnDef } from '@tanstack/react-table'
import { StatusCell } from './StatusCell'
// 导入其他cell组件...

export const columns: ColumnDef<Task>[] = [
    {
        accessorKey: "task_status",
        header: "Status",
        cell: StatusCell // 直接引用客户端组件
    },
    // 其他列...
]

关键注意点

  • TanStack Table的useReactTable是客户端钩子,必须在标记了'use client'的组件中使用。
  • 任何包含React元素渲染、客户端组件/钩子的代码,都不能在纯服务端组件中直接传递或执行。

内容的提问来源于stack exchange,提问作者Cigdan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:03:18