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:

https://i.sstatic.net/kLLij.png

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

Choose an image and save the selection information for the following page (Tarot card)

I'm in the process of creating a website that showcases multiple tarot cards. The goal is for users to select the cards they're interested in and have their chosen card displayed on the next page. I've implemented some code for selecting the ...

JavaScript Array Problem

Could you please review the code below and help me understand why I am encountering issues when trying to run the program? $(document).ready(function() { var comp = new Array("AAPL", "MSFT", "XRTX&"); var t = setInterval(function(){ ...

No data returned from API call in Next.js and Strapi

Recently, I encountered an issue with my Next.js frontend application that fetches data from a Strapi backend. Despite seeing requests being made in the Strapi developer logs, the retrieved data is empty. Below is a snippet of my Next.js code: import { us ...

When clicking on links that are not within the ng-view element, you will be redirected to the

I'm having trouble coming up with a name for this question, but here's my current situation This is the structure of my template: ├── index.html ├── ... ├── account │ ├── index.html │ ├── authorizat ...

Steps for adding a PHP dropdown menu to an HTML/Javascript webpage

Hey there, this is only my second post and I have to admit that I am a newbie in the world of web development. I'm spending countless hours scouring different websites for guidance, and I'm finally starting to grasp some concepts (at least for no ...

Performing a function inside a JSON structure

I am dealing with a JSON object that contains a list of functions I need to access and run like regular functions. However, I'm struggling to figure out how to achieve this. Here is what I have attempted: Bootstrapper.dynamic = { "interaction": f ...

Scrolling to the active list item in the navigation bar

Having an unordered list with the id mainul in the navigation bar is causing a problem when the page reloads. The navigation bar always starts at the beginning, but I would like it to automatically scroll to the active li element. This is my HTML code: & ...

Steps for adding SVGs to a <div> element

While there are numerous solutions on Stack Overflow detailing how to append an SVG in a div, none of them have proven successful for me. This could be due to my lack of experience with SVGs. Therefore, I am generating an SVG on my node server and the ...

Launching Node Application

While working with NestJS and IIS, I encountered an issue when deploying my 'dist' folder on the server using IISNode. The error message 'module not found @nestjs/core' prompted me to install the entire 'package.json' files (n ...

Guide to setting up collapsible sections within a parent collapsible

I came across this animated collapsible code that I'm using: https://www.w3schools.com/howto/howto_js_collapsible.asp Here is the HTML: <button type="button" class="collapsible">Open Collapsible</button> <div class="content"> &l ...

Navigating Redirects using axios in the Browser

Is there a way to work with redirects in axios to capture the redirected URL in the browser when making an API call? I am looking to retrieve the redirected URL through a GET request. ...

Best practice for integrating Typescript into an established ASP.NET 4 Webforms project

Currently, I am working on an older asp.net 4.0 Webforms project using Visual Studio 2015. My goal is to transition from using Javascript to TypeScript for certain client side code tasks. While I have experience using TypeScript in projects outside of Vis ...

Is there a way to display various data with an onClick event without overwriting the existing render?

In the process of developing a text-based RPG using React/Context API/UseReducer, I wanted to hone my skills with useState in order to showcase objects from an onclick event. So far, I've succeeded in displaying an object from an array based on button ...

How to achieve the functionality of ocibindbyname in JavaScript

I am currently utilizing an HTA page that is coded in JavaScript to monitor various Oracle tables. My goal is to optimize the Oracle query caching by using bind variables, similar to how I implemented it in a PHP environment with this code: $sql = "selec ...

Implementing a smooth camera movement in Three.js using the mousewheel

Is there anyone who can assist me with achieving smooth camera movement (forward/backward) using the mouse wheel? The current code I have is not providing the desired smoothness. document.addEventListener( 'mousewheel', onDocumentMouseWheel, fal ...

Modify a unique element within an array stored in the state using Redux toolkit

I'm currently attempting to modify a property of an object within an array stored in my state. export const changeStatus = createAsyncThunk('changeStatus', async (arg) => { const todo = arg const response = await axios.put(`${URL} ...

Server side pagination in AngularJS allows for dynamic loading of data

I am currently facing issues with slow application performance when retrieving large data from the database using Spring MVC and REST. I would like to implement server-side pagination in AngularJS to load only a subset of records. Could anyone provide gu ...

When attempting to start a new React Native project using npx, I encountered an error stating "react-native: command not found"

After running 'npx react-native init MyProject' for the first time, it prompted that react-native would be downloaded, but I mistakenly terminated the process. Now, when I try again, it shows an error saying 'react-native: command not found& ...

Struggling to retrieve information using the filter() method in MongoDB

I am currently attempting to retrieve the tasks assigned to a specific user using this setup router.get('/all', auth, async (req, res) => { try { const assignments_raw = await Assignment.find({listP: listP.indexOf(req.user.userId)}) ...

Building a dynamic attribute management system with vue and vuetify

In the backend business object, there is a custom attributes data structure that allows clients to add key/value pairs for storing in the database. For instance: Map<String, String> customAttributes; Here's an example of how it would look in th ...