Next.js 中"Functions cannot be passed directly to Client Components"错误解决方法
问题描述
在使用 Next.js(特别是 13.0.5+ 版本)时,当您尝试将函数作为 prop 传递给客户端组件时,可能会遇到以下错误:
Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server"
此错误通常出现在以下场景:
- 将包含函数的对象作为 prop 传递给客户端组件
- 在服务端渲染(SSR)页面中使用客户端组件时传递回调函数
error.(jsx|tsx)
组件未正确标记为客户端组件
典型示例代码:
jsx
// 在服务端组件中定义
matchesColumns.push({
field: 'pk',
headerName: "",
flex: 0.3,
renderCell: (params) => ( // 这是一个函数!
<Link href={`/matches/${encodeURIComponent(params.row.pk)}`}>
Details
</Link>
)
});
// 传递给客户端组件
<Table rows={matchesRows} columns={matchesColumns} />
解决方案
方案一:确保客户端组件标记为"use client"
在接收函数 prop 的组件文件最顶部添加 "use client" 指令:
jsx
"use client" // 在文件首行添加
import React from 'react';
function Table({ rows, columns }) {
// 组件实现
}
export default Table;
问题排查技巧
- 检查父组件是否是服务端组件(无 "use client")
- 确认所有接收函数的子组件都已标记 "use client"
- 对于动态路由页面中的组件更需特别注意,这些组件更容易出现此错误
方案二:标记函数为"use server"(Server Actions)
对于需要在客户端调用的服务端函数,使用 Next.js 14+ 的 Server Actions 功能:
jsx
"use server"; // 在文件顶部声明
import ClientComponent from './ClientComponent';
async function handleAction(data) {
"use server"; // 在函数内部再次声明
console.log('Server action executed', data);
// 服务端处理逻辑
}
export default function Page() {
return (
<ClientComponent action={handleAction} />
);
}
jsx
"use client";
export default function ClientComponent({ action }) {
return (
<button onClick={async () => {
await action("调用数据");
alert("操作成功!");
}}>
执行操作
</button>
);
}
警告
"use server" 需要 Next.js 13.4+ 版本,须在 next.config.js 中启用实验性功能:jsmodule.exports = { experimental: { serverActions: true } }
方案三:特殊文件(error.tsx)处理
当错误出现在 app/error.tsx
文件中时,必须将其标记为客户端组件:
tsx
"use client"; // 必须添加
type ErrorProps = {
error: Error;
reset: () => void; // 该回调由框架提供
}
export default function ErrorPage({ error, reset }: ErrorProps) {
return (
<div className="error-container">
<h2>发生错误!</h2>
<button onClick={reset}>重试</button>
</div>
);
}
最佳实践
组件边界清晰
- 服务端组件:数据处理、API 调用
- 客户端组件:交互逻辑、状态管理
合理使用服务端动作
jsx"use client"; export default function ProductCard({ product }) { // 图片加载器无需标记"use server" const imageLoader = ({ src }) => `https://api.example.com/${src.substring(src.indexOf("img"))}`; return ( <Image loader={imageLoader} src={product.imagePath} alt={product.name} width={300} height={300} /> ); }
优化函数传递
diff// 避免 <ClientComponent callback={() => { /* 逻辑 */ }} /> // 推荐 const handleAction = async (data) => { + "use server"; /* 服务端逻辑 */ } <ClientComponent action={handleAction} />
总结
问题场景 | 解决方案 | 适用条件 |
---|---|---|
普通客户端组件 | 添加 "use client" 指令 | Next.js 13+ |
服务端函数传递给客户端 | 标记函数 "use server" | Next.js 13.4+ |
error.tsx 文件问题 | 确保 "use client" 声明 | 所有版本 |
按照这些方案操作即可解决函数传递错误,同时确保应用遵循 Next.js 现代架构模式。