Can't get useFormState to work
Answered
WebDevSayantan posted this in #help-forum
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:
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
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. 😦
@WebDevSayantan Yes! It does, but now that runs into a different error as formData doesn't have all required values. 😦
then should be something wrong with your form
will need to see more code
@Ray then should be something wrong with your form
This is my full handleSubmit function:
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
<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.
@WebDevSayantan I'm calling formAction function at the end of submitHandler function with the prepared formData manually.
I think useFormState doesn't work with react-hook-form
form.handleSubmit() is calling preventDefault() which make the useFormStatus doesnt work properlywhy you need
useFormStatus when you can get the submission state from react-hook-form?That might be the case, I also removed the
The form submission functionality works as expected, only that the useFormState isn't behaving as I expect to.
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);
}
})}
>@Ray try this code
ts
<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);
}
})}
>
Form submission still works as expected, but the useFormStatus hook doesn't.
@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();
}
}}
>@WebDevSayantan I'm getting event.currentTaget as null inside the function.
could you show the code
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?either bind it to the server action or render a hidden input in the form
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.
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>
)@WebDevSayantan Figured it out.
My form doesn't need an onSubmit event anymore. useFormStatus works as expected.
typescript
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>
)
yeah that works if you doesn't need client side validation