-
-
Notifications
You must be signed in to change notification settings - Fork 444
feat(react-form): correctly handle client-side validation with NextJS server-action #1299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
I'm also wondering if it's a good idea to mutate the import { useForm } from '@tanstack/react-form';
import { useActionSubmit } from '@tanstack/react-form/nextjs';
import { action } from './actions';
export const ExampleForm = () => {
const form = useForm({ ... });
const handleActionSubmit = useActionSubmit(form);
return (
<form action={action} onSubmit={handleActionSubmit}>
...
</form>
);
}; |
View your CI Pipeline Execution ↗ for commit 5f2678b.
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! That's an interesting approach but I don't think exploiting a flag to submit the form twice is a sustainable way of doing that.
A possible direction I'd take is:
const form = useForm({
...formOpts,
onSubmitMeta: {} as FormData,
onSubmit: async ({ meta }) => {
startTransition(() => {
action(meta)
})
},
...
<form
action={action}
onSubmit={async (e) => {
e.preventDefault()
await form.handleSubmit(new FormData(e.currentTarget))
}}
>
Basically passing formData to the submit method that is executed only if validation (sync and async) passes.
What do you think?
Currently, there is no good, standardized way to properly handle client-side validation along with NextJS server-actions. The TanStack form supports server-actions, but the current implementation has one huge drawback: the server-action calls even if the client-side validation fails.
Client-side validation when submitting a form to a server-action is very important, especially for the user experience. The user sees validation errors immediately, instead of waiting for a response from the server. In addition, there is no reason to call a server-action if the client-side validation fails. The server will perform the same validation that takes place on the client side to eventually fail and return an error, so what is the purpose of pointlessly burdening the server?
So the reason for this feature is to make client-side validation with server-actions finally pleasant.
To prevent the server-action from executing,
event.preventDefault()
must be called synchronously. The problem is that form validation can also happen asynchronously, so there is no easy way to handle this. Soevent.preventDefault()
calls immediately, then form validation occurs, and if successful,event.target.requestSubmit()
is called to callonSubmit
again. In the next iteration ofonSubmit
the code knows that it has already been called and the validation has passed, so it does not callevent.preventDefault()
and the server-action can be executed.Some people have tried to implement this simply by calling
event.target.submit()
after successful validation, but with this approach the page is refreshed, so it completely fails with NextJS. I also noticed that callingonSubmit
again doesn't work without this line:await new Promise((resolve) => setTimeout(resolve, 0))
, so it's just a weird trick to make it work.Stopping the execution of the server-action must be done in
onSubmit
, because then we do not lose the progressive enhancement: if the form data is invalid and JavaScript is enabled, then validation is done on the client side, and the server-action call is blocked. Otherwise,onSubmit
cannot be called by the lack of JavaScript, so the server-action is called immediately, and all validation is done on the server, so everything still works correctly.