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

Next.js v13.4服务器组件加删除按钮报错,求组件拆分方案

问题描述

我开发了一个基于Next.js v13.4的应用,其中有一个Server Component(服务器组件)负责从数据库获取所有用户数据,并将每个用户展示在独立卡片中。我希望给每个卡片添加一个删除按钮,但为按钮添加点击事件监听器时出现以下错误:

Unhandled Runtime Error Error: Event handlers cannot be passed to Client Component props.

同时,尝试使用useRouter()时也报错:

Error: useRouter only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component

组件代码如下:

// import { useRouter } from "next/navigation";
import Link from "next/link";
import dbConnect from "../lib/dbConnect";
import User from "../models/User";

export default async function Home() {
    // const router = useRouter();
    const data = await getData();
    const users = data.props.users;

    const handleDelete = async (id: any) => {
        try {
            await fetch(`/api/users/${id}`, {
                method: "Delete",
            });
            // router.push("/");
        } catch (error) {
            console.log("error ===> ", error);
        }
    };

    return (
        <main className="main">
            <h1>Home page</h1>
            {users ? (
                <div className="users">
                    {users.map((user) => (
                        <div key={user._id}>
                            <div className="card">
                                <h3 className="user-name">Name: {user.name}</h3>
                                <p className="email">Email: {user.email}</p>
                                <p className="email">
                                    Password: {user.password}
                                </p>
                                <p className="email">Country: {user.country}</p>
                                <button onClick={(_id) => handleDelete(_id)}>Delete</button>
                            </div>
                        </div>
                    ))}
                    <Link className="add-btn-link" href="/new">
                        <div className="add-btn">
                            <p>+</p>
                        </div>
                    </Link>
                </div>
            ) : (
                <p>No users</p>
            )}
        </main>
    );
}

async function getData() {
    await dbConnect();

    /* find all the data in our database */
    const result = await User.find({});
    const users = result.map((doc) => {
        const user = doc.toObject();
        user._id = user._id.toString();

        return user;
    });

    return { props: { users: users } };
}

错误截图:
error screenshot

解决方案

核心是将带交互逻辑的部分拆分到Client Component,保留原Home组件作为Server Component负责数据获取,兼顾服务器渲染优势与客户端交互能力。

1. 创建用户卡片客户端组件

新建components/UserCard.tsx文件,顶部添加"use client"标记,将卡片UI、删除逻辑和路由操作移至此处:

"use client";

import { useRouter } from "next/navigation";

interface UserProps {
  user: {
    _id: string;
    name: string;
    email: string;
    password: string;
    country: string;
  };
}

export default function UserCard({ user }: UserProps) {
  const router = useRouter();

  const handleDelete = async () => {
    try {
      await fetch(`/api/users/${user._id}`, {
        method: "DELETE",
      });
      // 删除后刷新页面获取最新数据
      router.refresh();
    } catch (error) {
      console.log("error ===> ", error);
    }
  };

  return (
    <div className="card">
      <h3 className="user-name">Name: {user.name}</h3>
      <p className="email">Email: {user.email}</p>
      <p className="email">Password: {user.password}</p>
      <p className="email">Country: {user.country}</p>
      <button onClick={handleDelete}>Delete</button>
    </div>
  );
}

2. 修改Home服务器组件

保留原数据获取逻辑,引入并使用UserCard组件,移除原有交互代码:

import Link from "next/link";
import dbConnect from "../lib/dbConnect";
import User from "../models/User";
import UserCard from "../components/UserCard";

export default async function Home() {
  const data = await getData();
  const users = data.props.users;

  return (
    <main className="main">
      <h1>Home page</h1>
      {users ? (
        <div className="users">
          {users.map((user) => (
            <div key={user._id}>
              <UserCard user={user} />
            </div>
          ))}
          <Link className="add-btn-link" href="/new">
            <div className="add-btn">
              <p>+</p>
            </div>
          </Link>
        </div>
      ) : (
        <p>No users</p>
      )}
    </main>
  );
}

async function getData() {
  await dbConnect();

  const result = await User.find({});
  const users = result.map((doc) => {
    const user = doc.toObject();
    user._id = user._id.toString();
    return user;
  });

  return { props: { users: users } };
}

关键要点

  • "use client":必须放在客户端组件文件最顶部,告知Next.js该组件需在客户端运行,支持事件监听、hooks(如useRouter)。
  • 服务器组件专注数据获取:原Home组件保持为服务器组件,无需添加"use client",可直接在服务器端查询数据库,减少客户端请求开销。
  • router.refresh():删除用户后调用此方法刷新页面,获取最新用户列表,比router.push("/")更高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 05:14:59