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

Next.js服务端组件向客户端组件传函数组件Props报错求助

Next.js 服务端组件传递 React Icons 到客户端组件报错解决

问题场景

我把 <Bar /> 标记为客户端组件以使用 Framer Motion,该组件从单独的 data.ts 文件引入 React Icons。但父页面是服务端组件,页面加载时报错:

⚠️Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server"

相关代码

<Bar /> 组件代码

"use client";

import { ISkill } from "@/types";
import React from "react";
import { motion } from "framer-motion";

export default function Bar({ data: { name, level, Icon } }: { data: ISkill }) {
  const variants = {
    initial: {
      width: 0,
    },
    animate: {
      width: level,
      transition: {
        duration: 0.4,
        type: "spring",
        damping: 10,
        stiffness: 100,
      },
    },
  };

  return (
    <div className="text-white my-2 bg-gray-300 dark:bg-dark-300 rounded-full">
      <motion.div
        className="px-4 py-1 flex items-center rounded-full bg-gradient-to-r from-green to-blue-400"
        style={{ width: level }}
        variants={variants}
        initial="initial"
        animate="animate"
      >
        <Icon className="mr-3" />
        {name}
      </motion.div>
    </div>
  );
}

ISkill 接口定义

import { IconType } from "react-icons";

export interface ISkill {
  name: string;
  level: string;
  Icon: IconType;
}

父组件代码

<div className="my-2">
     {languages.map((language) => (
          <Bar data={language} key={language.name} />
     ))}
 </div>

data.ts 文件内容

import { FaPython} from "react-icons/fa";
import { BiCodeCurly } from "react-icons/bi";
import { ISkill } from "@/types";

export const languages: ISkill[] = [
  {
    name: "Python",
    level: "40%",
    Icon: FaPython,
  },
  {
    name: "C",
    level: "90%",
    Icon: BiCodeCurly,
  },
]

问题原因

服务端组件会在服务器上序列化后传递给客户端,而函数(包括 React Icons 这类函数组件)无法被序列化。当服务端组件直接把包含图标的数据传递给客户端组件时,就会触发这个错误。

解决方法

方法一:将 data.ts 标记为客户端模块

在 data.ts 顶部添加 "use client" 指令,让整个数据模块在客户端加载,避免服务端序列化函数:

"use client"; // 添加这行

import { FaPython} from "react-icons/fa";
import { BiCodeCurly } from "react-icons/bi";
import { ISkill } from "@/types";

export const languages: ISkill[] = [
  {
    name: "Python",
    level: "40%",
    Icon: FaPython,
  },
  {
    name: "C",
    level: "90%",
    Icon: BiCodeCurly,
  },
]

方法二:在客户端组件内部处理图标映射

把图标从数据文件中移除,改为在 <Bar /> 组件内部通过名称匹配图标:

  1. 修改 ISkill 接口:
export interface ISkill {
  name: string;
  level: string;
  iconName: string; // 替换原有的 Icon 字段
}
  1. 更新 data.ts:
import { ISkill } from "@/types";

export const languages: ISkill[] = [
  {
    name: "Python",
    level: "40%",
    iconName: "FaPython",
  },
  {
    name: "C",
    level: "90%",
    iconName: "BiCodeCurly",
  },
]
  1. 修改 <Bar /> 组件:
"use client";

import { ISkill } from "@/types";
import React from "react";
import { motion } from "framer-motion";
import { FaPython } from "react-icons/fa";
import { BiCodeCurly } from "react-icons/bi";

// 创建图标映射表
const IconMap = {
  FaPython,
  BiCodeCurly,
};

export default function Bar({ data: { name, level, iconName } }: { data: ISkill }) {
  const Icon = IconMap[iconName as keyof typeof IconMap];
  
  const variants = {
    initial: {
      width: 0,
    },
    animate: {
      width: level,
      transition: {
        duration: 0.4,
        type: "spring",
        damping: 10,
        stiffness: 100,
      },
    },
  };

  return (
    <div className="text-white my-2 bg-gray-300 dark:bg-dark-300 rounded-full">
      <motion.div
        className="px-4 py-1 flex items-center rounded-full bg-gradient-to-r from-green to-blue-400"
        style={{ width: level }}
        variants={variants}
        initial="initial"
        animate="animate"
      >
        {Icon && <Icon className="mr-3" />}
        {name}
      </motion.div>
    </div>
  );
}

方法三:将父页面改为客户端组件(不推荐)

如果父页面不需要服务端渲染的特性,可以在父页面顶部添加 "use client",让整个页面在客户端运行。但这种方法会丢失服务端组件的性能优势,仅适合简单页面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:17:39