The deletion function necessitates a switch from a server component to a client component

I built an app using Next.js v13.4. Within the app, there is a server component where I fetch all users from the database and display them in individual cards. My goal is to add a delete button to each card, but whenever I try to attach an event listener to the button, I encounter this 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.

Below is the code snippet for 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 } };
}

Attached below is a screenshot of the issue:

The error message also mentions that I can't use useRouter(). Here's the specific error:

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

To resolve this, I believe I need to split certain sections into separate components, although the exact approach eludes me.

Answer №1

When it comes to a server component, the best practice is to refrain from sending any JavaScript from the server itself. To ensure that the code for your click handler reaches the browser, a client component is required. Consider creating a CardDeleteButton.tsx file and adding the following:

"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 this component into your Home component and use it instead of your current button:

<CardButton id={user._id} />

By following this approach, you can maintain the page as a server component without compromising on functionality.

Answer №2

Another approach is to integrate the button within a form as a submit button. Include a concealed input for the item's identification. Trigger the form action upon submission and execute a server operation.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Customizing the Material-ui TextField DOM Element: A Step-by-Step

After trying the code below, I was expecting to see a yellowish "Hi," but instead received [object Object]. Is there a way to correct this issue? Possibly utilizing InputProps would help, but I haven't been able to locate a comprehensive tutorial on ...

The onChange event does not work as expected for Select controls

I am facing an issue with my common useForm.tsx file when handling the onChange event for select controls. The error message I encounter is displayed below. Does anyone have any suggestions on how to resolve this? Error: Type '(e: ChangeEvent<HTM ...

Update the variable obtained from the user input and insert it into a new container depending on the input value

In reference to my previous inquiries, I refrain from adding more details to avoid confusion since it already received numerous responses. While I can successfully retrieve input from a text field with the ID 'test' and display it in the 'r ...

Javascript chart

Hello everyone, I am diving into the world of fetch API. Currently, I am faced with a challenge where I need to generate the following list items: <li>Zimmerman, Paul</li> <li>Yimmerman, Raul</li> <li>Limmerman, Caul</li> ...

When utilizing a node.js TCP socket to receive and send modified data, the functionality only operates successfully on the initial attempt

I am attempting to create a node.js TCP socket that will receive data, modify it, and then send it back. I found a helpful example on Stack Overflow here: . The example works perfectly as is, but when I add my own code to send data back, it only works for ...

"The issue of Next.js not successfully retrieving a cookie from an Express response is arising specifically in

When trying to establish user tokens after logging in on my NextJS app (localhost:3005) in my local environment, I am facing an issue with the cookies not being populated in getServerSideProps. The response from my Express backend (localhost:3020) does inc ...

Example of fetching Pubnub history using AngularJS

I am not a paid PubNub user. I am utilizing the example code for an Angular JS basic chat application from PubNub, and I want to access the chat history. This specific example can be found on the PubNub website. git clone https://github.com/stephenlb/an ...

Difficulty sending a parameter to the onClick function of a React Button

I'm struggling with passing parameters to my callback function when clicking a material-ui button. Unfortunately, the following approach is not yielding the expected results. const fetchData = async (param) => { } <Button onClick={fetchData(&a ...

Send information using AJAX within a .NET integrated browser

In our current setup, we utilize a .NET embedded browser to showcase an HTML page that is generated by applying an XSLT file to an XML file using .NET. This process results in HTML content displayed within the embedded browser through the DocumentText prop ...

Is there a way to retrieve the $state object from ui router in the console?

I attempted to modify the route from the console by using this method to access the $state object: $inject = angular.injector(['ng', 'ui.router']); $inject.get('$state').go Unfortunately, I encountered an error: Uncaught Er ...

How can I call a function from one Vue component in another component?

I have developed a component with the function "logout" as seen in the code snippet below: // @/component/Painel.vue <template></template> <script> export default { name: 'panel', methods: { logout: function () { ...

Incorporate a map (using leafletjs or Google Maps) as a subtle backdrop

I am currently working on a one-page website and I would like to include a map as a background behind the "contact" section. The map can be set to float, draggable, or positioned at the back. I have experience using both the Google Maps API and LeafletJS, ...

The Next.js Image component is not compatible with an external URL as the image source

I've been struggling to use the image component in Next.js with an external URL as the source, but I keep encountering an error. I followed the instructions in the official Next.js documentation and updated the next.config.js file, but unfortunately, ...

Using Vue Js directive to implement a Select2 component

I've been exploring the example of the Vue.js wrapper component and trying to customize it to use a v-select2 directive on a standard select box, rather than creating templates or components for each one. You can view my implementation in this JS Bin ...

JavaScript/DOM - What sets apart a "CSS Selector" from an attribute?

When it comes to excluding declarative event handlers: <a href='#' onclick=<handler> ... /> Is there a significant difference between an Attribute and a CSS Selector? For example, if I define my own attribute: <a href='#&a ...

The type 'number[]' is lacking the properties 0, 1, 2, and 3 found in the type '[number, number, number, number]'

type spacing = [number, number, number, number] interface ISpacingProps { defaultValue?: spacing className?: string disabled?: boolean min?: number max?: number onChange?: (value: number | string) => void } interface IFieldState { value: ...

Working with conditional rendering in React Native allows us to easily change text based on different conditions. By utilizing

Hello fellow React Native beginners! I'm currently working on a feature where the text output on the screen changes based on the time of day. Specifically, I want the screen to display 'Morning' if it's morning, 'Afternoon' i ...

Is there a way for me to maintain a consistent layout across all pages while also changing the content component based on the URL route in Next.js?

I'm currently working with Typescript and Next.js My goal is to implement a unified <Layout> for all pages on my website. The layout comprises components such as <Header>, <Footer>, <Sidenav>, and <Content>. Here is the ...

Vue.js not responding to "mousedown.left" event listener

I am trying to assign different functionalities to right and left click on my custom element. Within the original code, I have set up event listeners for mouse clicks in the element file: container.addEventListener("mousedown", startDrag); conta ...

What methods can be utilized to conceal an if statement in code?

In my application, I am facing an issue with an external JavaScript file that contains multiple if statements. The problem arises when one of the if statements is still being executed even if the corresponding form is hidden. The flow of my application is ...