I’m trying to use useFormStatus()
to change UI dynamically when the form is submitted and it’s waiting to fetch the data.
I have a form component like
function onSubmit(data: z.infer<typeof FormSchema>) {
requestImg(data).then((imageStringify) => {
const binaryData = Buffer.from(imageStringify.image);
const imageBase64 = URL.createObjectURL(
new Blob([binaryData.buffer], { type: "image/jpeg" } /* (1) */)
);
setOpen(true);
setSource(imageBase64);
});
}
if (isDesktop) {
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="w-2/3 space-y-6"
>
...
with requestImg(data)
being a server action.
I’ve then the Button component nested inside the <form>
in a different Button.tsx file
import { useFormStatus } from "react-dom";
import { Button } from "@/components/ui/button";
export default function ButtonSub() {
const { pending } = useFormStatus();
console.log(pending);
return (
<Button className="w-full" type="submit" aria-disabled={pending}>
{pending ? "Processing..." : "Submit"}
</Button>
);
}
The issue is that, when I click submit, the button text doesn’t change to "Processing…"
EDIT
It seems that works only if you’re using the action keyword on the form. I’ve tried
<form
action={async (formData: FormData) => {
requestImg(formData).then((imageStringify) => {
const binaryData = Buffer.from(imageStringify.image);
const imageBase64 = URL.createObjectURL(
new Blob([binaryData.buffer], { type: "image/jpeg" } /* (1) */)
);
setSource(imageBase64);
setOpen(true);
});
}}
className="w-2/3 space-y-6"
>
but now the image generated with the byte8Array is broken.
I have an image with a dynamic src filled with
const [source, setSource] = useState("");
<img src={source}/>
2
Answers
A solution I found without using the action keyword but still using server action calling it as a function was to use
useTransition()
and the Submit Button client component
that is true.
useFormStatus
has to be used inside a child component of the form and it kinda watches the state change of the server action. But as far as I know it listens for theaction
.action
defines the server-side destination for the form data after submission. in the context of the server action, server actions make a post request to the current URL. so it is not listening for form onSubmit, that is why your first code execution is not working.if you read the enter link description here
see the action property, from the docs
Now server action runs in server context, they get executed on the server but
setState
is client specific code. what you have to do is you have to return a value. in your case:and using
useFormState
you can access to this state. from useFormState docsyou should be using this state value inside the component