-
Notifications
You must be signed in to change notification settings - Fork 47.7k
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
[React 19] allow opting out of automatic form reset when Form Actions are used #29034
Comments
I think you should return current values from action in such case and update the default value 😃 |
@adobe export issue to Jira project PWA |
I think you should return current values from action in such case and update the default value. and return required! |
This is very necessary in the step-by-step form, such as verifying the email in the auth form first |
Be careful to handle if the action throws an error, your "returning the new default" at the end of the function will be ineffective. |
The automatic form reset in React 19 actually caught me off guard, where in my case, I was trying to validate the form inputs on the server, then return & display the input errors on the client, but React will reset all my uncontrolled inputs. For context, I wrote a library just for doing server-side validation https://github.com/chungweileong94/server-act?tab=readme-ov-file#useformstate-support. I know that you can pass the original input ( It's easy to reset a form, but hard to restore a form. |
Now that I have played with React 19 form reset for a while, I think this behavior kind of forces us to write a more progressive enhancement code. This means that if you manually return the form data from the server and restore the form values, the user input will persist even without JavaScript enabled. Mixed feelings, pros and cons. |
what about using onSubmit as well the action to prevent default? |
If you want to opt-out of automatic form reset, you should continue using +function handleSubmit(event) {
+ event.preventDefault();
+ const formData = new FormData(event.target);
+ startTransition(() => action(formData));
+}
...
-<form action={action}>
+<form onSubmit={handleSubmit}> -- That way you still opt-into transitions but keep the old non-resetting behavior. And if you're a component library with your own action-based API that wants to maintain form-resetting behavior, you can use // use isPending instead of `useFormStatus().pending`
const [isPending, startTransition] from useTransition();
function onSubmit(event) {
// Disable default form submission behavior
event.preventDefault();
const form = event.target;
startTransition(async () => {
// Request the form to reset once the action
// has completed
ReactDOM.requestFormReset(form);
// Call the user-provided action prop
await action(new FormData(form));
})
} --https://codesandbox.io/p/sandbox/react-opt-out-of-automatic-form-resetting-45rywk We haven't documented that yet in https://react.dev/reference/react-dom/components/form. It would help us a lot if somebody would file a PR with form-resetting docs. |
@eps1lon do you think using I am surprised by this new default behavior here, because this forces essentially everyone to use So if this reset behavior is a 100% set in stone for React 19, why not suggest using |
@rwieruch I'm not sure this is true. As @acdlite mentions in the PR below, it's for uncontrolled inputs.
Controlled inputs are probably in almost every form case still desirable with RSC (as Sebastian mentions "I will also say that it's not expected that uncontrolled form fields is the way to do forms in React. Even the no-JS mode is not that great.") Also, this is about "not diverging from browser behavior", as @rickhanlonii mentions in more discussion over on X here: But it does indeed seem to be a controversial choice to match browser behavior and reset uncontrolled fields. |
EDIT: I have written about the solution over HERE. Thanks for the input here @karlhorky and putting all the pieces together. I have seen that this matches the native browser more closely, so I see the incentive for this change. Just wanted to double check here, because I am re-adjusting my teaching material again (my own fault here, because we are still quite early on this :)). So if I am not using a third-party library for forms or actions, would the following code look good for upserting an entity with form + server action, if I still would want to use the const TicketUpsertForm = ({ ticket }: TicketUpsertFormProps) => {
const [actionState, action] = useActionState(
upsertTicket.bind(null, ticket?.id),
{ message: "" }
);
return (
<form action={action} className="flex flex-col gap-y-2">
<Label htmlFor="title">Title</Label>
<Input
id="title"
name="title"
type="text"
defaultValue={
(actionState.payload?.get("title") as string) || ticket?.title
}
/>
<Label htmlFor="content">Content</Label>
<Textarea
id="content"
name="content"
defaultValue={
(actionState.payload?.get("content") as string) || ticket?.content
}
/>
<SubmitButton label={ticket ? "Edit" : "Create"} />
{actionState.message}
</form>
);
}; And then the action returns the const upsertTicketSchema = z.object({
title: z.string().min(1).max(191),
content: z.string().min(1).max(1024),
});
export const upsertTicket = async (
id: string | undefined,
_actionState: {
message: string;
payload?: FormData;
},
formData: FormData
) => {
try {
const data = upsertTicketSchema.parse({
title: formData.get("title"),
content: formData.get("content"),
});
await prisma.ticket.upsert({
where: {
id: id || "",
},
update: data,
create: data,
});
} catch (error) {
return {
message: "Something went wrong",
payload: formData,
};
}
revalidatePath(ticketsPath());
if (id) {
redirect(ticketPath(id));
}
return { message: "Ticket created" };
}; EDIT: I think that's something @KATT wanted to point out in his proposal: #28491 (comment) |
Yup, that’s pretty much it. This way it works the same if submitted before hydration happens |
Resetting the form automatically is a real head-scratcher. How should we preserve the state of a form when errors occur? Using Using controlled components defeats the purpose of The example here is deceptively simple, as there are no visible form inputs. What am I missing? |
The docs are misleading on this topic because on the React 19 docs, it's the React 18 canary version that is shown as an example which does not reset the form. https://19.react.dev/reference/react-dom/components/form#handling-multiple-submission-types |
Automatic form reset only applies when passing functions to the The original issue description isn't explicit about this. @LutherTS If there was a change in behavior to APIs available in previous React stable versions, please include a reproduction. |
@eps1lon You're correct, the feature has only been available since the React 18 canary version so it's only going to be breaking for those using the canary version. However, the canary version is the default version running on Next.js, so the change may be breaking for a significant number of codebases there. |
The same thing doesn't apply to NextJS app router tho, where both |
Sure, but that would be an issue for Next.js. I don't think we rolled this change out in a 14.x Next.js stable release. The automatic form reset was enabled in #28804 which was included in vercel/next.js#65058 which is not part of any stable Next.js release as far as I can tell. |
OK, so what you're saying is this behavior only happens in Next.js 15 RC which uses React 19 RC, both of which being currently unstable, and therefore this is a trade-off for using unstable versions. Then at the very least the React 19 docs should reflect these changes. And I reiterate that if these changes are reflected in the React 19 docs, the entire example for "Handling multiple submission types" is completely irrelevant, because there is no point in saving a draft if after saving said draft it disappears from the textarea. So how does the React team reconcile presenting a feature for one purpose when the actual feature currently does the exact opposite? |
True, fair enough.
Yes, it is not. But that's the whole points right, where we feedback on a feature before stable release. I do think that auto form reset behaviour does bring some benefits in terms of progressive enhancement, but if you think again, React is kinda doing extra stuff unnecessarily. By default, the browser will reset the form when we submit it, then when we submit a form via JS(React), it retains the form values after submit, but React then artificially reset the form. Yes, form reset is a cheap operation, but why not make it an option for people to opt-in instead of doing it automatically. |
And that's certainly appreciated. Though there's an important difference between a change in behavior and the behavior of a new feature. The comments here read as though this breakage is not the norm when we didn't change any behavior between stable, SemVer minor React releases nor between stable, SemVer minor Next.js releases. Changes in behavior between Canary releases should be expected. Now that we established that this isn't a change in behavior, we can discuss the automatic form reset. The reason this was added was that it matches the native browser behavior before hydration or with no JS (e.g. when |
I have no minimal example at hand, but it seems like |
That seems like something you should be able to highlight starting with https://react.new. Though I believe |
This can be handled using |
One of the original examples from the react team was a save draft button. If you clicked this (especially if you coded in a hotkey for it), I could see it creating unintentional behavior. It's also worth noting server actions are only as fast as your server is, and if that server isn't vercel (or you have a really slow db connection from that), saving might not always be super fast. When submitting a form I don't see it as a huge issue, but saving (even for smth like a user profile), can become an issue. I also sometimes notice a problem with what I input after I click submit when filling out forms that will trigger an error, so I start editing while it's still submitting. Overall though, the defaultValue method is probably fine, just not my preferred method of implementation. I do think it fares better/worse in different scenarios though, so if it doesn't matter a lot in your implementation, have at it. Edit: Re-reading your message, I do see your concern about returning errors that are no longer valid. It's definitely a trade-off, with both resulting in a bad user experience. But if you move away from actions to client-side validation (with server-side as well), you can cut down on API calls and solve both issues in one swoop. |
Personally, I've gone back to just using onSubmit. Doesn't support progressive enhancement, but realistically my website isn't functional without js anyways. It also supports both frontend and backend validation, but (obviously) doesn't have anywhere near the DX of the native stuff. It's a shame, but still works fine and doesn't require controlled inputs. |
Currently I use this workaround to tackle with this break change. At least, It works. Besides, I don't think return the payload from useActionState is a good idea, it makes things complicated if you need to transform your data. |
use this video by lee rob from next js it wil help you https://www.youtube.com/watch?v=KhO4VjaYSXU |
For small forms the solution here make sense but for Large forms? Please no! |
Forms are broken with action!I wanted to implement Zod validation with data persistence in case of validation error else I don't mind the form rest. referring to this video: https://www.youtube.com/watch?v=KhO4VjaYSXU Thus, It's better to validate on the client first and then send data to the server to revalidate, that will reduce unnecessary API Well, to handle Client-side validation like react-hook-form, with Zod and the useActionState hook leads to excessive boilerplate. We really need a workaround that. |
@devdatkumar the requests aren't really redundant though? just add basic input validation (required, email, number, min, max etc) - and then let the server handle the more complex stuff. 90% of the validation required in forms is one of these anyway. |
@JClackett what if i want to implement zod for font-end? this is what i came up with.
well, the other way is (as you said) to implement basic validation, but then again i need to write regex for every component. |
@devdatkumar but why do all this? just use the built in html input validation and submit the form, what do you mean write regex for every component?? |
This video only covers a very basic, server-centered scenario. I think the community does not have any problems with understanding these patterns. However, forms in many real-world applications typically require a bit more interactivity. Where I see the community currently having problems is building client-side interactivity around these new server-centered building blocks. Unmounting a form or closing a dialog after completing the Action, resetting the state of a dialog that internally used |
Don't you think the built-in html input validation code will be cultured for validation below.
|
The way react and next js handles forms is about server side only but we may want also client side validation just like how react-hook-form works.But the problem is that it uses onSubmit and it can't be used with useActionState.A simple sln i think we can use built in Html validation as our client side validation.or use this library https://conform.guide/ works with action attribute,zod and useActionState but idon't think it is ready for production. |
@shemaikuzwe https://conform.guide/ doesn't really have clean code. but i what i really prefer is a solution from the react team / @leerob. for example the scenario I ran into: on client side, do not reset form if Zod validation fails, so that user can fix data and then finally submit.
|
I wonder what's reason behind this change. The expected user's behaviour is to retain their inputs in case there's an error. And how does it work with returning 25MB PDF from the server when there's an error in another field? 🤔 |
That's to me consider a huge file tho, overtime it's will cost you a lot of bandwidth, you might want to change the strategy? What I usually do in this case is that I will upload the file to the server separately, then return a file ID for the rest of the form submission. |
That's what I've been saying since before React 19 became stable. The solution for me – and really what I think is the solution so far – is doing it like @leerob and @rwieruch before initially recommended, which is using a Server Action via onSubmit with event.preventDefault() instead. Because indeed, Server Actions (meaning Server Functions sent to the client then wrapped into startTransition or managed with useActionState) can be used on any listener, not just on the action prop of the form or the formAction prop of a button. ...Or you can always use React Router v7 where the inputs are retained after the action and can be dismissed on success by handling the actionData. |
syntactic: semantic: type validation: now if i just consider built-in html input validation, how do i confirm sematic validation? not only that, even if i use sematic validation, it doesn't display what is missing, it just highlight invalid data by default. so, for me to display what's missing / wrong with data, |
@devdatkumar I get it, but I would say that its not reacts job to consider that you want to use zod/yup as a validating library - so yeah you will need to use onSubmit + prevent default and handle errors manually - orrr just do server side validation only and not mess around with it :) there will eventually be a form library that integrates with it all and provides a seamless experience but react 19 is new so it will take time. I think there will probably always be caveats/boilerplate as trying to merge server/client side experiences into one thing may just not be possible - but who knows |
Hello all I just wrote a simple library the purpose of which is have server side validation (with zod), client side native validation and maintain form values across submits. |
While I applaud the effort ... am I crazy for thinking it SHOULD NOT require a separate library just to have the client retain knowledge it already has (instead of arbitrarily discarding it after every server communication)? 😦 |
Summary
repo: https://github.com/stefanprobst/issue-react-19-form-reset
react 19@beta currently will automatically reset a form with uncontrolled components after submission. it would be really cool if there was a way to opt out of this behavior, without having to fall back to using controlled components - especially since component libraries (e.g.
react-aria
) have invested quite a bit of time to work well as uncontrolled form elements.the main usecase i am thinking of are forms which allow saving progress, or saving a draft, before final submit. currently, every "save progress" would reset the form.
The text was updated successfully, but these errors were encountered: