Skip to content

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;

问题排查技巧

  1. 检查父组件是否是服务端组件(无 "use client")
  2. 确认所有接收函数的子组件都已标记 "use client"
  3. 对于动态路由页面中的组件更需特别注意,这些组件更容易出现此错误

方案二:标记函数为"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 中启用实验性功能:

js
module.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>
  );
}

最佳实践

  1. 组件边界清晰

    • 服务端组件:数据处理、API 调用
    • 客户端组件:交互逻辑、状态管理
  2. 合理使用服务端动作

    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}
        />
      );
    }
  3. 优化函数传递

    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 现代架构模式。