Next.js 14:如何在Server Component中使用可复用按钮组件?
解决Next.js 14中Server Component使用带onClick的Client Component问题
问题核心
Next.js 14的Server Component无法直接向Client Component传递函数类型的props(比如onClick),因为服务端无法序列化函数并传递给客户端,这就是你遇到报错的根本原因。
解决方案
方案一:抽离交互逻辑到独立Client子组件
创建一个包含交互逻辑的小型Client Component,页面(保持Server Component)只负责渲染这个子组件,无需添加'use client'。
示例代码:
- 创建
InteractiveButton.tsx(Client Component):
"use client" import Button from './Button' export default function InteractiveButton() { const handleOnClick = () => { console.log(111); } return <Button onClick={handleOnClick}>Test</Button> }
- 修改根页面
Home.tsx(Server Component):
import InteractiveButton from '@/components/InteractiveButton' export default function Home() { return ( <main> <InteractiveButton /> </main> ) }
方案二:让Button组件内置可配置的交互逻辑
如果按钮的交互逻辑可以通过非函数类型的props配置,直接把逻辑写到Button组件内部,避免页面传递函数。
示例代码:
- 修改Button组件:
"use client" import { ButtonHTMLAttributes } from "react" type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & { logMessage?: string } export default function Button({ logMessage, children, ...rest }: ButtonProps) { const handleClick = () => { if (logMessage) { console.log(logMessage) } rest.onClick?.() // 保留原有onClick的执行逻辑 } return ( <button onClick={handleClick} {...rest}> {children} </button> ) }
- 页面使用(Server Component):
import { Button } from '@/components' export default function Home() { return ( <main> <Button logMessage="111">Test</Button> </main> ) }
方案三:使用React Server Actions(服务端交互场景)
如果点击逻辑需要调用服务端代码(如数据库操作、API调用),可以用Server Actions替代客户端事件处理函数,无需在页面传递函数。
示例代码:
- 定义Server Action:
"use server" export async function handleServerClick() { // 服务端逻辑,比如写入数据库、调用外部API等 console.log('Server side log: 111') }
- 修改Button组件:
"use client" import { ButtonHTMLAttributes } from "react" import { handleServerClick } from './actions' export default function Button({ children }: ButtonHTMLAttributes<HTMLButtonElement>) { return ( <button onClick={handleServerClick}> {children} </button> ) }
- 页面直接渲染Button即可,保持Server Component属性。
总结
核心思路是避免在Server Component中直接传递函数给Client Component,通过封装Client子组件、配置可序列化props、使用Server Actions三种方式,既能保留页面的Server Component特性,又能实现按钮的交互功能。
内容的提问来源于stack exchange,提问作者xtreebug
相关产品推荐
相关产品推荐

