Enhance form documentation for form default reset behavior#8512
Enhance form documentation for form default reset behavior#8512MaxwellCohen wants to merge 13 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.
| Give each input a `name` attribute so its value is included in `FormData`. You can use [uncontrolled](/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form) inputs instead of controlled `value`/`onChange` pairs, and you don't need an `onSubmit` handler or `e.preventDefault()`. | ||
|
|
||
| Pass a function to the `action` prop of form to run the function when the form is submitted. [`formData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) will be passed to the function as an argument so you can access the data submitted by the form. This differs from the conventional [HTML action](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action), which only accepts URLs. Unlike `onSubmit`, an `action` runs in a [Transition](/reference/react/useTransition) and calling `e.preventDefault()` isn't needed. After the `action` function succeeds, all uncontrolled field elements in the form are reset. | ||
| This extends the [HTML `action` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action), which only accepts a URL, to also accept a function. Because the form stays a native HTML form, it can be submitted before JavaScript loads; with a [Server Function](/reference/rsc/server-functions), it works without JavaScript enabled. |
There was a problem hiding this comment.
ecause the form stays a native HTML form, it can be submitted before JavaScript loads; with a [Server Function](/reference/rsc/server-functions), it works without JavaScript enabled
this part is a bit unclear and hard to parse!
There was a problem hiding this comment.
Great point. When I read it 3 times last week, it seemed so clear ;) now it is not
udpate in b341728
There was a problem hiding this comment.
I feel you on that 😂
|
|
||
| * Runs the submission function in a [Transition](/reference/react/useTransition). | ||
| * Tracks pending state so child components can read it with [`useFormStatus`](/reference/react-dom/hooks/useFormStatus). | ||
| * Sends thrown errors to the nearest error boundary. |
There was a problem hiding this comment.
Not sure if "sends" is the right terminology for this 🤔
| * Runs the submission function in a [Transition](/reference/react/useTransition). | ||
| * Tracks pending state so child components can read it with [`useFormStatus`](/reference/react-dom/hooks/useFormStatus). | ||
| * Sends thrown errors to the nearest error boundary. | ||
| * Works with [`useActionState`](/reference/react/useActionState) and [`useOptimistic`](/reference/react/useOptimistic) for form state and optimistic UI. |
There was a problem hiding this comment.
This bullet seems out of place in its framing, since the intro to the list is When you pass a function to action, React: but here "works" is not answering that question!
| ### Display a pending state during form submission {/*display-a-pending-state-during-form-submission*/} | ||
| ### Preserving form values after submission {/*preserve-form-values-after-submission*/} | ||
|
|
||
| The browser clears a form's input state on submit. A URL `action` follows this same behavior. React does the same when `action` is a function, so your form works consistently before and after JavaScript loads. |
There was a problem hiding this comment.
this paragraph reads a little strange, and the behavior was explained better above!
|
|
||
| </Sandpack> | ||
|
|
||
| Because you call the action manually from `onSubmit`, [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) won't report its pending state. Read `isPending` from the same [`useTransition`](/reference/react/useTransition) call instead. |
There was a problem hiding this comment.
Hm, i thought useFormStatus worked regardless, also with onSubmit?
There was a problem hiding this comment.
yep, I got just using onSubmit without action confused
| 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.
|
|
||
| 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>
);
}
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