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 /> 组件内部通过名称匹配图标:
- 修改
ISkill接口:
export interface ISkill { name: string; level: string; iconName: string; // 替换原有的 Icon 字段 }
- 更新
data.ts:
import { ISkill } from "@/types"; export const languages: ISkill[] = [ { name: "Python", level: "40%", iconName: "FaPython", }, { name: "C", level: "90%", iconName: "BiCodeCurly", }, ]
- 修改
<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
相关产品推荐
相关产品推荐

