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

Next.js服务端组件传参至客户端遇toJSON对象不支持警告问题

Next.js App Router + Sequelize 数据传递警告的原因及解决

问题背景

我在Next.js App Router中用Sequelize从MySQL取数据,getTransactions方法返回结构如下:

const { rows, count } =  await Transaction.findAndCountAll({
    limit,
    offset,
    where: whereClause,
    order: [['created_at', 'DESC']],
});

return {
    items: rows,
    totalItems: count,
    totalPages: Math.ceil(count / limit),
    currentBalance,
};

把这份数据传给客户端组件时,控制台弹出警告:

Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.

服务端组件代码:

const data = await getTransactions({ page, limit, query: search });
return (
    <TransactionsTable response={data} />
);

客户端组件代码:

const TransactionsTable = ({ response }: { response : PaginatedTransactions | null }) => {
    // TSX 渲染逻辑
}

我通过序列化+反序列化,手动把created_at和updated_at转回Date类型,解决了警告问题,处理代码如下:

const data = await getTransactions({ page, limit, query: search });
const serialize = JSON.stringify(data);
const deserialize = JSON.parse(serialize, (key, value) => {
    if (key === "created_at" || key === "updated_at") {
        return new Date(value);
    }
    return value;
}) as PaginatedTransactions | null;

<TransactionsTable response={deserialize} />

但我不明白为什么必须做这些额外操作?


原因解析

Sequelize的findAndCountAll返回的rows数组里,每一条记录都不是普通JS对象,而是Sequelize封装的Model实例对象。这些实例自带toJSON()等自定义方法,还有Sequelize内部的属性和逻辑,属于「带额外能力的复杂对象」,不符合Next.js对跨组件传递props的要求。

Next.js App Router在服务端组件向客户端组件传递数据时,本质是把数据序列化为JSON格式在服务端和客户端之间传输。为了保证传输的可靠性和性能,Next.js明确限制:只能传递纯对象、字符串、数字这类「可直接序列化的简单值」,不能传递带自定义方法或内部状态的复杂对象——这类对象无法被正确序列化,而且客户端也不需要服务端Model实例的额外方法。

你做的序列化+反序列化操作,本质是把Sequelize的复杂实例转成了纯对象:

  1. JSON.stringify(data)会自动调用Model实例的toJSON()方法,剥离掉Sequelize的内部属性和方法,只保留数据字段;
  2. JSON.parse()把JSON字符串转回纯JS对象,同时你手动把被序列化转成字符串的日期字段还原成了Date类型(JSON本身不支持Date类型,序列化时会自动转成字符串)。

处理后传给客户端的就是符合要求的纯对象,所以警告消失了。


更简洁的替代方案

其实不用手动做JSON序列化/反序列化,Sequelize自带了把实例转成纯对象的方法:

  • 单个实例用instance.get({ plain: true });
  • 批量处理数组用rows.map(row => row.get({ plain: true }))。

修改getTransactions方法即可:

const { rows, count } =  await Transaction.findAndCountAll({
    limit,
    offset,
    where: whereClause,
    order: [['created_at', 'DESC']],
});

// 把Sequelize实例批量转成纯对象
const plainItems = rows.map(row => row.get({ plain: true }));

return {
    items: plainItems,
    totalItems: count,
    totalPages: Math.ceil(count / limit),
    currentBalance,
};

这样返回的items就是纯对象数组,直接传给客户端组件不会触发警告,还能保留日期字段的Date类型,省去了手动序列化反序列化的步骤。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 04:52:32