Next.js Discord

Discord Forum

Can't get useFormState to work

Answered
WebDevSayantan posted this in #help-forum
Open in Discord
I'm using Server Actions with react-hook-form and somehow can't get useFormState to work for showing pending state.

CreateInvoice & Submit Button component:
export default function InvoiceForm() {
  const [state, formAction] = useFormState(
    generateInvoice.bind(null, invoice ? "update" : "create"),
    { success: false },
  );
  const form = useForm<z.infer<typeof invoiceSchema>>({
    resolver: zodResolver(invoiceSchema),
    defaultValues,
  })
  return (
    <Form {...form}>
      <form action={formAction} 
        onSubmit={(event) => {
          event.preventDefault();
          form.handleSubmit(() => {
            // formData processing and calling server action
            formAction(formData);
          }
      >
      {/* some form controls */}
      <SubmitButton action={invoice ? "update" : "create"} />
      </form>
    </Form>
  )
}
function SubmitButton({ action }: { action: "create" | "update" }) {
  const { pending } = useFormStatus();
  return (
      {/* This pending state is not working */}
      <Button type="submit" aria-disabled={pending}>
        {pending
          ? `${action === "create" ? "Generating" : "Updating"} Invoice...`
          : `${action === "create" ? "Generate" : "Update"}  Invoice`}
      </Button>
  );
}
Answered by Ray
form.handleSubmit() is calling preventDefault behind the scenes which made useFormStatus doesn't work
View full answer

32 Replies

@Ray try comment out `event.preventDefault();`
No, that didn't work too.
@WebDevSayantan No, that didn't work too.
does it work if you comment out the onSubmit props
@Ray does it work if you comment out the `onSubmit` props
Yes! It does, but now that runs into a different error as formData doesn't have all required values. 😦
will need to see more code
@Ray then should be something wrong with your form
This is my full handleSubmit function:
<form
  action={formAction}
  onSubmit={(event) => {
    form.handleSubmit(() => {
      const formData = new FormData();
      if (invoice?.id) {
        formData.append("id", `${invoice.id}`);
      }
      formData.append("guestName", form.getValues("guestName"));
      formData.append(
        "invoiceDate",
        form.getValues("invoiceDate").toString(),
      );
      formData.append(
        "checkinDate",
        form.getValues("checkinDate").toString(),
      );
      formData.append(
        "checkoutDate",
        form.getValues("checkoutDate").toString(),
      );
      formData.append("homestayId", homestayId);
      formData.append(
        "accomodation",
        JSON.stringify(form.getValues("accomodation")),
      );
      formData.append(
        "food",
        JSON.stringify({
          breakfast: form.getValues("food.breakfast"),
          lunch: form.getValues("food.lunch"),
          dinner: form.getValues("food.dinner"),
          snacks: form.getValues("food.snacks"),
        }),
      );
      formAction(formData);
    })(event);
  }}
  className="flex flex-col w-full gap-8"
>


Github repo to the actual file if needed: https://github.com/Ishanz23/the-pineapple-home-nextjs/blob/main/app/business/%5Bhomestay%5D/invoices/components/invoiceForm.tsx
I'm calling formAction function at the end of submitHandler function with the prepared formData manually.
form.handleSubmit() is calling preventDefault() which make the useFormStatus doesnt work properly
why you need useFormStatus when you can get the submission state from react-hook-form?
That might be the case, I also removed the
action={formAction}
action prop from the form as it's not actually being used.
The form submission functionality works as expected, only that the useFormState isn't behaving as I expect to.
try this code
 <form
    action={action}
    onSubmit={form.handleSubmit((values) => {
      form.trigger();
      if (form.formState.isValid) {
        const formData = new FormData();
        for (const [k, v] of Object.entries(values)) {
          formData.append(k, v);
        }
        action(formData);
      }
    })}
  >
@WebDevSayantan Form submission still works as expected, but the useFormStatus hook doesn't.
ok made it work with this
 <form
    action={formAction}
    onSubmit={(e) => {
      form.trigger();
      if (form.formState.isValid) {
        e.currentTarget?.requestSubmit();
      } else {
        e.preventDefault();
      }
    }}
  >
I'm getting event.currentTaget as null inside the function.
and does it work?
the currentTarget is null if you log it in console
Sure..
const handleFormAction = (event: FormEvent<HTMLFormElement>) => {
  form.handleSubmit((values) => {
    form.trigger();
    console.log("is valid", form.formState.isValid);
    if (form.formState.isValid) {
    console.log(values);
    const formData = new FormData();
    if (invoice?.id) {
      formData.append("id", `${invoice.id}`);
    }
    formData.append("guestName", form.getValues("guestName"));
    formData.append(
      "invoiceDate",
      form.getValues("invoiceDate").toString(),
    );
    formData.append(
      "checkinDate",
      form.getValues("checkinDate").toString(),
    );
    formData.append(
      "checkoutDate",
      form.getValues("checkoutDate").toString(),
    );
    formData.append("homestayId", homestayId);
    formData.append(
      "accomodation",
      JSON.stringify(form.getValues("accomodation")),
    );
    formData.append(
      "food",
      JSON.stringify({
        breakfast: form.getValues("food.breakfast"),
        lunch: form.getValues("food.lunch"),
        dinner: form.getValues("food.dinner"),
        snacks: form.getValues("food.snacks"),
      }),
    );
    formData.append(
      "amenities",
      JSON.stringify(form.getValues("amenities")),
    );
    // formAction(formData);
    console.log(event);
    event.currentTarget.requestSubmit();
  } else {
    event.preventDefault();
  }
})(event);
};

  <form
    ref={formRef}
    action={formAction}
    onSubmit={handleFormAction}
    className="flex flex-col w-full gap-8"
  >
form.handleSubmit() is calling preventDefault behind the scenes which made useFormStatus doesn't work
Answer
@WebDevSayantan Sure.. typescript const handleFormAction = (event: FormEvent<HTMLFormElement>) => { form.handleSubmit((values) => { form.trigger(); console.log("is valid", form.formState.isValid); if (form.formState.isValid) { console.log(values); const formData = new FormData(); if (invoice?.id) { formData.append("id", `${invoice.id}`); } formData.append("guestName", form.getValues("guestName")); formData.append( "invoiceDate", form.getValues("invoiceDate").toString(), ); formData.append( "checkinDate", form.getValues("checkinDate").toString(), ); formData.append( "checkoutDate", form.getValues("checkoutDate").toString(), ); formData.append("homestayId", homestayId); formData.append( "accomodation", JSON.stringify(form.getValues("accomodation")), ); formData.append( "food", JSON.stringify({ breakfast: form.getValues("food.breakfast"), lunch: form.getValues("food.lunch"), dinner: form.getValues("food.dinner"), snacks: form.getValues("food.snacks"), }), ); formData.append( "amenities", JSON.stringify(form.getValues("amenities")), ); // formAction(formData); console.log(event); event.currentTarget.requestSubmit(); } else { event.preventDefault(); } })(event); }; tsx <form ref={formRef} action={formAction} onSubmit={handleFormAction} className="flex flex-col w-full gap-8" >
  const handleFormAction = (event: FormEvent<HTMLFormElement>) => {
    form.trigger();
    console.log("is valid", form.formState.isValid);
    if (form.formState.isValid) {
      event.currentTarget.requestSubmit();
    } else {
      event.preventDefault();
    }
  };
Okay, Got it. useFormStatus is working now. But I need to modify the formData before calling the server action.
event.currentTarget.requestSubmit();
^ Is there anyway to pass the modified formData to the formAction call here?
InvoiceId, homestayId, calendar inputs from shadCn and the nested formGroups. Will try to see workarounds. Anyway, thanks a lot, Got to know the why the useFormState is not working.
@Ray np
Figured it out.

My form doesn't need an onSubmit event anymore. useFormStatus works as expected.

const [state, formAction] = useFormState(
  generateInvoice.bind(null, invoice ? "update" : "create"),
  { success: false },
);

const handleFormAction = (data: FormData) => {
  const formData = new FormData();
  // modifying formdata according to my needs
  formAction(formData);
}
return ( 
  <form action={handleFormAction} className="flex flex-col w-full gap-8">
    ...
  </form>
)