Update form documentation for forms default reset behavior#8512
Update form documentation for forms default reset behavior#8512MaxwellCohen wants to merge 17 commits into
Conversation
Size changesDetails📦 Next.js Bundle Analysis for react-devThis analysis was generated by the Next.js Bundle Analysis action. 🤖 This PR introduced no changes to the JavaScript bundle! 🙌 |
|
Going to dig a bit into the current behavior of this, so might take some time to review! Thank you! |
…ction` prop and its behavior: - including the handling of `FormData`, - the necessity of `name` attributes, - the advantages of using a function over a URL. - removed redundant information about `e.preventDefault()` for the `action` prop.
| When a user taps a specific button, the form is submitted, and a corresponding action, defined by that button's attributes and action, is executed. For instance, a form might submit an article for review by default but have a separate button with `formAction` set to save the article as a draft. | ||
| When a button without `formAction` submits the form, React calls the form's `action`. When a button with `formAction` submits the form, React calls that button's action instead. For example, the form below publishes an article by default, but its **Save draft** button stores the current content without publishing it. | ||
|
|
||
| In this example the draft is held in state, so the saved content stays in the textarea after you submit it. In a real app you would persist the draft on the server. Pass a [Server Function](/reference/rsc/server-functions) (a function marked with [`'use server'`](/reference/rsc/use-server)) to `formAction` to save the draft from the server, optionally combined with [`useActionState`](/reference/react/useActionState) to track its pending state and result. |
There was a problem hiding this comment.
(a function marked with 'use server')
is this a common pattern in the docs?
There was a problem hiding this comment.
Most of the examples are called out in the code update it in code, so moving to code and using useActionState
947c926
There was a problem hiding this comment.
To be clear, sorry if this was too biref, i was wondering about the usage of the () inline linking of in that way!!
There was a problem hiding this comment.
But i think its better the way you did it regardless
| function EditForm() { | ||
| // The action returns { submitted: formData, error } on failure | ||
| const [state, formAction] = useActionState(submitForm, {}); | ||
| const values = state.submitted ?? new FormData(); |
There was a problem hiding this comment.
Why do we need a seperate variable for values? Is this pattern better than returning the values from the useActionState function?
https://aurorascharff.no/posts/handling-form-validation-errors-and-resets-with-useactionstate/
https://www.robinwieruch.de/react-server-action-reset-form/
There was a problem hiding this comment.
Good point, I forgot about optional chaining (one of my favorite JS features when working on making the examples)
7288a0d to
08359a5
Compare
08359a5 to
947c926
Compare
| export default function Search() { | ||
| function handleSubmit(e) { | ||
| // Prevent the browser from reloading the page | ||
| e.preventDefault(); |
There was a problem hiding this comment.
Why was this e.preventDefault() removed? 🤔
There was a problem hiding this comment.
I removed e.preventDefault() to show that, by default, forms reset their values. My goal is to progressively disclose the behavior of forms. React extends HTML form behavior with the onSubmit and action props, not replaces it. I think this is the brilliance of React 19 form APIs.
React is at its best when it takes something that was hard, such as updating content on the page or building reusable components, and makes it easy (sometimes too easy with useEffect 😉 ). I see the form APIs as the next step of this.
If it is unclear, or better to have it, I am open to suggestions.
There was a problem hiding this comment.
hmm yeah i wasn't sure why this was removed either. I don't really have strong feelings either way tho
|
|
||
| When you pass a function to `action` or `formAction`, React resets the form's [uncontrolled fields](/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form) after the action succeeds. This reset only affects uncontrolled fields—[inputs controlled with state](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable) are never cleared. | ||
|
|
||
| To preserve the values of uncontrolled fields after submission, add an `onSubmit` handler that calls `e.preventDefault()` and runs the action inside a [Transition](/reference/react/useTransition). Keep the `action` prop on the form so it continues to work before JavaScript loads. |
There was a problem hiding this comment.
I just tried this and it calls both: the submitForm action and the handleSubmit function.
If we want the form to progressively enhance and at the same time preserving values after submission, I believe the right answer is useActionState.
"use client";
import { useActionState } from "react";
import { someServerFunction } from "./some-server-function";
export default function EditForm() {
const [state, dispatchAction, isPending] = useActionState(someServerFunction, {
title: "My draft",
});
return (
<form action={dispatchAction}>
<input name="title" defaultValue={state.title} />
<button type="submit" disabled={isPending}>
{isPending ? "Saving..." : "Save"}
</button>
</form>
);
}There was a problem hiding this comment.
Thank you very much for looking into this.
handleSubmit includes submitForm so both functions will be called in onSubmit.
This comment made me think we should show multiple examples of resetting a form rather than just one. So I added a Recipe with 3 examples of state-keeping. 2cb1370
| export default function EditForm() { | ||
| const [isPending, setIsPending] = useState(false); |
There was a problem hiding this comment.
I think this is a pattern we should stop recommending now that we have better primitives, so I would just drop this example and leave the other two which are "Async React compliant". What do you think?
There was a problem hiding this comment.
Very good point. I removed this example in a3cd240
| ### Handling form submission with an event handler {/*handle-form-submission-with-an-event-handler*/} | ||
|
|
||
| Pass a function to the `onSubmit` event handler to run code when the form is submitted. By default, the browser sends the form data to the current URL and refreshes the page, so call [`e.preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) to override that behavior. | ||
| Pass a function to the `onSubmit` event handler to run code when the form is submitted. By default, the browser sends the form data to the current URL and refreshes the page. Call [`e.preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) in your handler to override that behavior. |
There was a problem hiding this comment.
maybe change to in your event handler instead of just handler thought its probably pretty obvious
|
|
||
| ### Preserving form values after submission {/*preserve-form-values-after-submission*/} | ||
|
|
||
| By default, the browser clears a form's input state after submission. Forms with a URL `action` follow this behavior, and React mirrors it when `action` is a function-ensuring your form behaves consistently both before and after JavaScript loads. |
There was a problem hiding this comment.
This should probably be 2 seperate words function-ensuring?
|
|
||
| #### `onSubmit` and `action` {/*with-onsubmit-and-usetransition*/} | ||
|
|
||
| Adding an `onSubmit` handler that calls `e.preventDefault()` will run instead of the `action` prop. |
There was a problem hiding this comment.
so if you don't preventDefault it will run the action prop function? onSubmit doesn't always override?
There was a problem hiding this comment.
That is correct; you can use onSubmit as a logging check, so you would not want to override the default browser behavior. I added a comment to better explain the behavior.
|
|
||
| The `onSubmit` approach above keeps every uncontrolled field. When you need finer control, two other patterns are available: | ||
|
|
||
| * **Reset from your own Action API.** If you build an Action-based API and still want the form to reset after the Action runs, call the `requestFormReset` API from `react-dom` with the form element inside the Transition. |
There was a problem hiding this comment.
make this issue to get it documented #8529
Summary
Documents React’s default form-reset behavior when using the function
action/formActionprops, and explains how to preserve field values when you don’t want that reset. Applying @rickhanlonii's feedback from #7795 and building off of work @aurorascharff recentlly did.actionprop section, and a new Troubleshooting entry (“Why does my form reset when I use an action?”). React resets uncontrolled fields after a successful action, matching browser<form action="...">behavior (including before JS loads).e.preventDefault()inonSubmit, then run the action manually insideuseTransitionwhile keeping theactionprop for progressive enhancement.requestFormResetfromreact-dom, and returning submittedFormDatafrom server actions to restore values viadefaultValue(withuseActionState).key/defaultValuekeep textarea content after “Save draft.”Closes #8397
Alternative to #7795 and #8465