Delete button requires me to turn server component to client component

I have an app in Next.js v13.4. In the app, I have a server component where I get all users from the database and display them in separate cards. I want each card to have a delete button but when I add an event listener on the button, it gives me an error:

Unhandled Runtime Error Error: Event handlers cannot be passed to Client Component props.
  <button onClick={function} children=...>
                  ^^^^^^^^^^
If you need interactivity, consider converting part of this to a Client Component.

Here’s the code of my 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 } };
}

Here’s the screenshot of the photo:

error screenshot

It’s also telling me that I can’t use useRouter(). Here’s the error message:

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

I think I need to separate some parts to a separate component but I’m not sure how.

>Solution :

For a server component, the idea is to not send any JavaScript coming from it. For the code of your click handler to make it to the browser, you need a client component. You could create a CardDeleteButton.tsx in which you add:

"use client";

import { useRouter } from "next/navigation";

export default function CardDeleteButton.tsx({ id }: { id: string }) {
  const router = useRouter();
  const handleDelete = async (id: string) => {
    try {
      await fetch(`/api/users/${id}`, {
        method: "Delete",
      });
      router.push("/");
    } catch (error) {
      console.log("error ===> ", error);
    }
  };

  return <button onClick={() => handleDelete(id)}>Delete</button>;
}

Import it in your Home component and call it in place of your current button:

<CardButton id={user._id} />

This way you can keep the page as a server component.

Leave a Reply