Skip to content

Commit 5245923

Browse files
committed
Enhance form documentation for form default reset behavior
1 parent 152a471 commit 5245923

1 file changed

Lines changed: 111 additions & 13 deletions

File tree

  • src/content/reference/react-dom/components

src/content/reference/react-dom/components/form.md

Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ To create interactive controls for submitting information, render the [built-in
3838

3939
`<form>` supports all [common element props.](/reference/react-dom/components/common#common-props)
4040

41-
[`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): a URL or function. When a URL is passed to `action` the form will behave like the HTML form component. When a function is passed to `action` the function will handle the form submission in a Transition following [the Action prop pattern](/reference/react/useTransition#exposing-action-props-from-components). The function passed to `action` may be async and will be called with a single argument containing the [form data](https://developer.mozilla.org/en-US/docs/Web/API/FormData) of the submitted form. The `action` prop can be overridden by a `formAction` attribute on a `<button>`, `<input type="submit">`, or `<input type="image">` component.
41+
[`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action): a URL or function. When a URL is passed to `action` the form will behave like the [HTML action](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form#action). When a function is passed to `action` the function will handle the form submission in a Transition following [the Action prop pattern](/reference/react/useTransition#exposing-action-props-from-components). The function passed to `action` may be async and will be called with a single argument containing the [form data](https://developer.mozilla.org/en-US/docs/Web/API/FormData) of the submitted form. The `action` prop can be overridden by a `formAction` attribute on a `<button>`, `<input type="submit">`, or `<input type="image">` component.
4242

4343
#### Caveats {/*caveats*/}
4444

4545
* When a function is passed to `action` or `formAction` the HTTP method will be POST regardless of value of the `method` prop.
46+
* When a function is passed to `action` or `formAction`, React resets all [uncontrolled](/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form) field elements after the action succeeds. See [Preserve form values after submission](#preserve-form-values-after-submission).
4647

4748
---
4849

@@ -59,7 +60,8 @@ This example reads the submitted values with [`new FormData(e.target)`](https://
5960
```js src/App.js
6061
export default function Search() {
6162
function handleSubmit(e) {
62-
// Prevent the browser from reloading the page
63+
// Prevent the default browser behavior
64+
// of reloading the page and resetting the form
6365
e.preventDefault();
6466

6567
// Read the form data
@@ -88,7 +90,7 @@ Reading form data with `onSubmit` works in every version of React and gives you
8890

8991
### Handle form submission with an action prop {/*handle-form-submission-with-an-action-prop*/}
9092

91-
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.
93+
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, React resets all [uncontrolled](/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form) field elements in the form. To keep their values, see [Preserve form values after submission](#preserve-form-values-after-submission).
9294

9395
<Sandpack>
9496

@@ -109,6 +111,85 @@ export default function Search() {
109111

110112
</Sandpack>
111113

114+
### Preserve form values after submission {/*preserve-form-values-after-submission*/}
115+
116+
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.
117+
118+
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. The reset only applies to uncontrolled fields, so [inputs you control with state](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable) are never cleared.
119+
120+
To keep the values of uncontrolled fields, 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 still works before JavaScript loads.
121+
122+
<Sandpack>
123+
124+
```js src/App.js
125+
import { useTransition } from "react";
126+
import { submitForm } from "./api.js";
127+
128+
export default function EditForm() {
129+
const [isPending, startTransition] = useTransition();
130+
131+
function handleSubmit(e) {
132+
// Stop React from resetting the form after the action succeeds
133+
e.preventDefault();
134+
const formData = new FormData(e.target);
135+
startTransition(async () => {
136+
await submitForm(formData);
137+
});
138+
}
139+
140+
return (
141+
<form action={submitForm} onSubmit={handleSubmit}>
142+
<input name="title" defaultValue="My draft" />
143+
<button type="submit" disabled={isPending}>
144+
{isPending ? "Saving..." : "Save"}
145+
</button>
146+
</form>
147+
);
148+
}
149+
```
150+
151+
```js src/api.js hidden
152+
export async function submitForm(formData) {
153+
await new Promise((res) => setTimeout(res, 1000));
154+
}
155+
```
156+
157+
</Sandpack>
158+
159+
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.
160+
161+
<DeepDive>
162+
163+
#### Resetting only some fields, or resetting on the server {/*resetting-only-some-fields*/}
164+
165+
The `onSubmit` approach keeps every uncontrolled field. When you need finer control, two other patterns are available:
166+
167+
* **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.
168+
169+
* **Reset to server-provided values.** When an action validates input on the server, return the submitted `FormData` and pass it to each field's `defaultValue`. React restores those values instead of clearing them, and the form keeps working before JavaScript loads:
170+
171+
```js
172+
import { useActionState } from "react";
173+
import { submitForm } from "./actions.js";
174+
175+
function EditForm() {
176+
// The action returns { submitted: formData, error } on failure
177+
const [state, formAction] = useActionState(submitForm, {});
178+
const values = state.submitted ?? new FormData();
179+
return (
180+
<form action={formAction}>
181+
<input name="title" defaultValue={values.get("title") ?? ""} />
182+
{state.error && <p>{state.error}</p>}
183+
<button type="submit">Save</button>
184+
</form>
185+
);
186+
}
187+
```
188+
189+
Return the original `FormData` object rather than a new one so React can restore the values even before JavaScript has loaded.
190+
191+
</DeepDive>
192+
112193
### Handle form submission with a Server Function {/*handle-form-submission-with-a-server-function*/}
113194
114195
Render a `<form>` with an input and submit button. Pass a Server Function (a function marked with [`'use server'`](/reference/rsc/use-server)) to the `action` prop of form to run the function when the form is submitted.
@@ -372,28 +453,45 @@ Learn more about updating state from a form action with the [`useActionState`](/
372453
373454
### Handling multiple submission types {/*handling-multiple-submission-types*/}
374455
375-
Forms can be designed to handle multiple submission actions based on the button pressed by the user. Each button inside a form can be associated with a distinct action or behavior by setting the `formAction` prop.
456+
A form can have more than one submit button, each running a different action. Set the `formAction` prop on a `<button>` to override the `<form>`'s `action` when that button submits the form.
457+
458+
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.
376459
377-
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.
460+
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.
378461
379462
<Sandpack>
380463
381464
```js src/App.js
382-
export default function Search() {
465+
import { useState } from 'react';
466+
467+
export default function ArticleForm() {
468+
const [draft, setDraft] = useState(() => new FormData());
469+
383470
function publish(formData) {
384-
const content = formData.get("content");
385-
const button = formData.get("button");
386-
alert(`'${content}' was published with the '${button}' button`);
471+
const content = formData.get('content');
472+
alert(`'${content}' was published!`);
473+
// Clear the saved draft after publishing.
474+
setDraft(new FormData());
387475
}
388476

389477
function save(formData) {
390-
const content = formData.get("content");
391-
alert(`Your draft of '${content}' has been saved!`);
478+
const content = formData.get('content');
479+
alert(`Your draft of '${content}' was saved!`);
480+
// Keep the submitted content as the current draft.
481+
setDraft(formData);
392482
}
393483

484+
const savedContent = draft.get('content') || '';
394485
return (
395486
<form action={publish}>
396-
<textarea name="content" rows={4} cols={40} />
487+
<textarea
488+
// Changing the key resets the textarea to the saved draft.
489+
key={`${savedContent}-content`}
490+
name="content"
491+
rows={4}
492+
cols={40}
493+
defaultValue={savedContent}
494+
/>
397495
<br />
398496
<button type="submit" name="button" value="submit">Publish</button>
399497
<button formAction={save}>Save draft</button>
@@ -402,4 +500,4 @@ export default function Search() {
402500
}
403501
```
404502
405-
</Sandpack>
503+
</Sandpack>

0 commit comments

Comments
 (0)