Search code examples
typescriptnext.jsreact-hook-formzodshadcnui

I am having trouble getting my server action invoked. Getting error: A React form was unexpectedly submitted


I am trying to build a registration form with Next.js, react-hook-form, and Zod. I have the following code for the form component:

'use client';

import { z } from "zod";
import { useRef } from "react";
import { useForm } from "react-hook-form";
import { useFormState, useFormStatus } from "react-dom";
import { zodResolver } from "@hookform/resolvers/zod";
import { registerSchema } from "@/lib/schemas";
import { RegisterProps } from "@/lib/definitions";

function RegisterForm({ onRegister }: RegisterProps) {
  const formRef = useRef<HTMLFormElement>(null);
  const form = useForm<z.infer<typeof registerSchema>>({
    resolver: zodResolver(registerSchema),
    defaultValues: {
      fullName: "",
      email: "",
      password: "",
    },
  });

  const [state, registerAction] = useFormState(onRegister, { message: "" });

  return (
    <Form {...form}>
      <form
        ref={formRef}
        action={registerAction}
        className="space-y-4"
        onSubmit={form.handleSubmit(() => formRef?.current?.submit())}
      >
      ...
     </form>
    </Form>
  );
}

My validation works fine, but my action (registerAction) never runs. I get the following error when I hit submit on the form:

Error: A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you're trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().

I have tried using the requestSubmit() method. The form does not submit, but I don't get the error anymore. I am trying to understand why this happens. Calling the action as in the code snippet below, my action is invoked and the user is registered. Why does the method below work?

const onSubmit = (data: z.infer<typeof registerSchema>) => {
  const formData = new FormData()
  formData.set('fullName', data.fullName;
  formData.set('email', data.email;
  formData.set('password', data.password;

  registerAction(formData)
}

return (
  <Form {...form}>
    <form
      ref={formRef}
      className="space-y-4"
      onSubmit={form.handleSubmit(onSubmit)}
    >
    ...
    </form>
  </Form>
);

Solution

  • handleSubmit will preventDefault and receive the form data if form validation is successful. before invoking "onSubmit"

    react-hook-form give you all this By default so you don't need ref you can just pass your server action in handleSubmit

      <Form {...form}>
        <form
          className="space-y-4"
          onSubmit={form.handleSubmit((values)=> registerAction(values) )}
        >
        ...
        </form>
      </Form>
    

    and You can pass an async function and await res from server

    const onSubmit = async (data) => {
      try {
        const res = await registerAction(values)
        consloe.log(res)
      } catch (e) {
        // handle your error
      }
    };
    
      <Form {...form}>
        <form onSubmit={form.handleSubmit(onSubmit(data)} >
        ...
        </form>
      </Form>