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:
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.