reactjsnext.jsnext.js13toast

nextjs and react-hot-toast issue


import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
// import ToastProvider from "./(components)/Toastify";
import { Toaster } from "react-hot-toast";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        {children}
        <Toaster />
      </body>
    </html>
  );
}

"use client";
import { getDebugger } from "@/app/lib/debugger";
import { registerNewUser } from "@/app/lib/actions";
import FormInput from "../../(components)/forms/FormInput";
import SubmitBtn from "../../(components)/forms/SubmitBtn";
// import { toast } from "react-toastify";
import toast from "react-hot-toast";
import { useFormState } from "react-dom";

const debug = getDebugger("sign-up-form");

const initialState = {
  message: null,
};

const Page = () => {
  const [state, formAction] = useFormState(registerNewUser, initialState);
  return (
    <main>
      <form action={formAction}>
        <FormInput
          name="firstName"
          label="First Name:"
          type="text"
          placeholder="Your First Name"
        />
        <FormInput
          name="lastName"
          label="Last Name:"
          type="text"
          placeholder="Your Last Name"
        />
        <FormInput
          name="password"
          label="Password:"
          type="password"
          placeholder="******"
        />
        <FormInput
          name="confirmPassword"
          label="Confirm Password:"
          type="password"
          placeholder="******"
        />
        <FormInput
          name="email"
          label="Email:"
          type="email"
          placeholder="Your Email Here"
        />
        <SubmitBtn text="Sign Up" />
        {state?.message && toast.error(state.message)}
      </form>
    </main>
  );
};
export default Page;

"use server";
import { customFetch } from "./customFetch";
import { getDebugger } from "./debugger";

const debug = getDebugger("actions");

export const registerNewUser = async (prevState: any, formData: FormData) => {
  const user = {
    firstName: formData.get("firstName") as string,
    lastName: formData.get("lastName") as string,
    password: formData.get("password") as string,
    confirmPassword: formData.get("confirmPassword") as string,
    email: formData.get("email") as string,
  };

  try {
    const { data } = await customFetch.post("/api/auth/sign-up", user);
    return data;
  } catch (error: any) {
    return { message: error.response.data.error };
  }
};

enter image description here

Watch the image aswell please, the number next to the button and the X times that the toast is displaying

Im trying to implement toastify inside my nextJS app, but for some reason Im getting more then 1 display of the toast, for example i got two times toastify sometimes, sometimes one, sometimes three, and so on..

but one annoying issue that im stuck on for 5 hours! is that im seeing a number next to my button, you can see that in the image, and every time I click the sign up, the number increase with 1-2-3 (depeneds on the number of the toast displaying)

Please help me solve that, if you need the github repo : https://github.com/Parselinho/handy

Thanks in advance


Solution

  • That's because {state?.message && toast.error(state.message)}

    You must not put any execuatable code withen JSX. JSX is recreated every time the component re-renders.

    You have two issues in the code:

    1. The logical and &&

    When using logical and && in JSX, It'll check the left side if it is true, it will print the right side. toast.error(...) returns the id of the toast (number) so it's printed in the dom as you can see. It's increased because every time the component re-renders it creates new toast with new id.

    1. Execuatble code in JSX

    You must never call any function other than components withen JSX.

    To achieve what you are looking for, you can use useEffect

    useEffect(() => {
      if (state.message) toast.error(state.message)
    }, [state.message])