Comments (22)
If you want to opt-out of automatic form reset, you should continue using onSubmit
like so:
+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 ReactDOM.requestFormReset
:
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.
from react.
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 (FormData
#28754) back to the client, but it's not easy to reset the form based on the previously submitted FormData, especially when the form is somewhat complex, I'm talking about things like dynamic items inputs, etc.
It's easy to reset a form, but hard to restore a form.
from react.
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 action
attribute on the form?
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 payload
in the case of an error, so that the form can show this as the defaultValue
s, so that it does not reset.
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)
from react.
what about using onSubmit as well the action to prevent default?
from react.
Automatic form reset only applies when passing functions to the action or formAction prop. A new feature that wasn't available before React 19.
The same thing doesn't apply to NextJS app router tho, where both action
& formAction
is available and marked as stable via React 18 canary for over a year or two, so it's pretty unfair to most NextJS users, where they kinda get screwed by the way NextJS/React handles the feature rollout or versioning.
from react.
Sure, but that would be an issue for Next.js.
True, fair enough.
I don't think we rolled this change out in a 14.x Next.js stable release.
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.
from react.
I think you should return current values from action in such case and update the default value 😃
from react.
@adobe export issue to Jira project PWA
from react.
I think you should return current values from action in such case and update the default value. and return required!
from react.
This is very necessary in the step-by-step form, such as verifying the email in the auth form first
from react.
Be careful to handle if the action throws an error, your "returning the new default" at the end of the function will be ineffective.
from react.
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.
from react.
@eps1lon do you think using onSubmit
over action
is the right call here? A bit of context:
I am surprised by this new default behavior here, because this forces essentially everyone to use onSubmit
over action
, because everyone wants to keep their form values intact in case of an (validation) error.
So if this reset behavior is a 100% set in stone for React 19, why not suggest using useActionState
then with a payload
object then where all the form values in the case of an error are sent back from the action so that the form can pick these up as defaultValue
s?
from react.
this forces essentially everyone to use
onSubmit
overaction
, because everyone wants to keep their form values intact in case of an (validation) error
@rwieruch I'm not sure this is true.
As @acdlite mentions in the PR below, it's for uncontrolled inputs.
It has no impact on controlled form inputs.
- Source: #28804
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.
from react.
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
action
attribute on the form?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
payload
in the case of an error, so that the form can show this as thedefaultValue
s, so that it does not reset.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" }; };
Yup, that’s pretty much it. This way it works the same if submitted before hydration happens
from react.
Resetting the form automatically is a real head-scratcher. How should we preserve the state of a form when errors occur?
Using defaultValue
doesn't work on all input types (e.g. <select>
).
Using controlled components defeats the purpose of useActionState()
.
The example here is deceptively simple, as there are no visible form inputs.
What am I missing?
from react.
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
This in this very example completely defeats the purpose of saving a draft. So rather than allowing opting out of automatic form reset, I believe it's the reset itself that should be an option. Because the current decision is tantamount to breaking every form that would upgrade to React 19.
from react.
Because the current decision is tantamount to breaking every form that would upgrade to React 19.
Automatic form reset only applies when passing functions to the action
or formAction
prop. A new feature that wasn't available before React 19.
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.
from react.
@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.
But what is most important to me then is that the React docs need to correctly reflect these changes at the very least on their https://19.react.dev/ site. Because again, automatically resetting forms not only defeat the entire purpose of the example being shown (https://19.react.dev/reference/react-dom/components/form#handling-multiple-submission-types) they're also not being reflected in the example which actually runs on React 18 canary instead of React 19 (https://codesandbox.io/p/sandbox/late-glade-ql6qph?file=%2Fsrc%2FApp.js&utm_medium=sandpack).
from react.
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.
from react.
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?
from react.
Yes, it is not. But that's the whole points right, where we feedback on a feature before stable release.
And that's certainly appreciated. Though there's an important different 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 <form action="/some-endpoint">
) would be used. Maybe we should focus why using onSubmit
as shown in #29034 (comment) doesn't work in that case?
from react.
Related Issues (20)
- [React 19] Different behaviors with `preload` method HOT 2
- Bug: React 18 SSR Sometimes fallback to CSR without throw any errors on Prod Mode, The fiber node which tag is "HostRoot"(3)has flags "Snapshot"(1024),it works fine when flags is Update(4) HOT 1
- Bug: ForwardRef components cant have display names or types HOT 2
- Consider changing the next property type of the update object of the dispatchSetState function from any to null
- [React 19]
- ..
- [DevTools Bug]: 5.2.0 is not available for Firefox HOT 2
- [DevTools Bug] getCommitTree(): Invalid commit "1" for root "445". There are only "1" commits.
- [eslint-plugin-react-hooks] Missing type declarations HOT 2
- [React 19]
- Bug: useEffect is triggered even if the array as dependency variable wasn't changed. HOT 4
- [DevTools Bug]: React Devtools not working neither on vite or cra project HOT 5
- ..
- Bug: Error Recovery Mechanism Overwriting Initial Rendering Errors in Concurrent Mode HOT 1
- Bug: Empty `style={}` object values cause hydration warnings in React 18.3.1 - Includes solution
- [React 19] Cannot assign to readonly property HOT 8
- Unexpected Initial State Jump in 'useEffect" with 'setTimeout' and State Dependencies HOT 3
- React[19] Module '"react"' has no exported member 'useActionState'. HOT 2
- Bug: effect runs with stale state values outside of Concurrent React HOT 1
- Feature Request: ESLint hooks rule for accessing previous state when deriving new state
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from react.