Skip to content

Commit 7ad9c1d

Browse files
committed
update Preserving form values after an Action section based on feedback
1 parent 8463596 commit 7ad9c1d

1 file changed

Lines changed: 91 additions & 23 deletions

File tree

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

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

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ To create interactive controls for submitting information, render the [built-in
4848
#### Caveats {/*caveats*/}
4949

5050
* If you pass a function to `action` or `formAction`, the HTTP method will be `POST` regardless of the value of the `method` prop.
51-
* If you pass a function 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 [Preserving form values after submission](#preserve-form-values-after-submission).
51+
* If you pass a function 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 [Preserving form values after an Action](#preserve-form-values-after-an-action).
5252

5353
---
5454

@@ -101,7 +101,7 @@ When you pass a function to `action`, React:
101101
* Runs the function in a [Transition](/reference/react/useTransition), keeping the page responsive.
102102
* Makes the pending state available to child components via [`useFormStatus`](/reference/react-dom/hooks/useFormStatus).
103103
* Propagates any errors to the nearest Error Boundary.
104-
* Resets the form's uncontrolled fields when the function succeeds. To keep their values, see [Preserving form values after submission](#preserve-form-values-after-submission).
104+
* Resets the form's uncontrolled fields when the function succeeds. To keep their values, see [Preserving form values after an Action](#preserve-form-values-after-an-action).
105105

106106
Because the Action runs in a Transition, you can also use [`useActionState`](/reference/react/useActionState) to manage form state and [`useOptimistic`](/reference/react/useOptimistic) for optimistic UI. For [Server Functions](/reference/rsc/server-functions) and progressive enhancement, see [Handling form submission with a Server Function](#handle-form-submission-with-a-server-function).
107107

@@ -385,27 +385,39 @@ If the form is submitted before JavaScript loads, React includes the Server Func
385385

386386
---
387387

388-
### Preserving form values after submission {/*preserve-form-values-after-submission*/}
388+
### Preserving form values after an Action {/*preserve-form-values-after-an-action*/}
389389

390-
Submitting a form with a URL `action` clears its input state. React mirrors this behavior when `action` is a function by resetting the form's [uncontrolled fields](/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form) after the Action succeeds. When a Server Function progressively enhances a form, this keeps its behavior consistent before and after JavaScript loads. [Inputs controlled with state](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable) are not cleared.
390+
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. To keep the submitted values, store each value in state and pass it to that field's `defaultValue`. The reset then restores each field to its `defaultValue` instead of clearing it.
391391

392-
#### Restore fields with `useActionState` {/*with-useactionstate*/}
392+
To keep those values when users submit the form before JavaScript loads, render the `<form>` from a [Client Component](/reference/rsc/use-client) and pass a [Server Function](/reference/rsc/server-functions) to [`useActionState`](/reference/react/useActionState). Pass the Action it returns to the form's `action` prop. The Server Function receives the previous state, followed by the submitted `FormData`, as shown in [Displaying a form submission error without JavaScript](#display-a-form-submission-error-without-javascript). Return an object with the values to keep, and pass each value, such as `state.title`, to the matching field's `defaultValue`:
393393

394-
Pass the Action returned by [`useActionState`](/reference/react/useActionState) to the `action` prop. Return the values you want to keep from your Action, and pass them to each field's `defaultValue`. The automatic form reset restores those default values instead of clearing the fields.
394+
```js
395+
// actions.js
396+
'use server';
395397

396-
<Sandpack>
398+
import { saveTitle } from './api.js';
399+
400+
export async function updateTitle(previousState, formData) {
401+
const title = formData.get('title');
402+
await saveTitle(title);
403+
return { title };
404+
}
405+
```
406+
407+
```js
408+
// EditForm.js
409+
'use client';
397410

398-
```js src/App.js
399411
import { useActionState } from 'react';
400-
import { submitForm } from './api.js';
412+
import { updateTitle } from './actions.js';
401413

402414
export default function EditForm() {
403-
const [state, dispatchAction, isPending] = useActionState(submitForm, {
415+
const [state, updateTitleAction, isPending] = useActionState(updateTitle, {
404416
title: 'My draft',
405417
});
406418

407419
return (
408-
<form action={dispatchAction}>
420+
<form action={updateTitleAction}>
409421
<input name="title" defaultValue={state.title} />
410422
<button type="submit" disabled={isPending}>
411423
{isPending ? 'Saving...' : 'Save'}
@@ -415,30 +427,86 @@ export default function EditForm() {
415427
}
416428
```
417429

418-
```js src/api.js hidden
419-
export async function submitForm(previousState, formData) {
420-
await new Promise((res) => setTimeout(res, 1000));
421-
return {
422-
title: formData.get('title'),
423-
};
430+
Users can submit the form before JavaScript loads. After the Action succeeds, `defaultValue={state.title}` restores the title from the returned object.
431+
432+
For a form that submits only after JavaScript loads, store the values with [`useState`](/reference/react/useState). Update that state when the Action finishes. React applies the new `defaultValue`, then resets the form, so each field shows the submitted value:
433+
434+
<Sandpack>
435+
436+
```js src/App.js
437+
import { useState } from 'react';
438+
439+
export default function EditForm() {
440+
const [title, setTitle] = useState('My draft');
441+
442+
async function saveAction(formData) {
443+
await new Promise((res) => setTimeout(res, 1000));
444+
setTitle(formData.get('title'));
445+
}
446+
447+
return (
448+
<form action={saveAction}>
449+
<input name="title" defaultValue={title} />
450+
<button type="submit">Save</button>
451+
</form>
452+
);
424453
}
425454
```
426455

427456
</Sandpack>
428457

458+
Call a Server Function from `saveAction`, then store the string it returns. Because `saveAction` is defined in the Client Component, users can submit this form only after JavaScript loads:
459+
460+
```js
461+
// actions.js
462+
'use server';
463+
464+
import { saveTitle } from './api.js';
465+
466+
export async function submitTitle(formData) {
467+
const title = formData.get('title');
468+
await saveTitle(title);
469+
return title;
470+
}
471+
```
472+
473+
```js
474+
// EditForm.js
475+
'use client';
476+
477+
import { useState } from 'react';
478+
import { submitTitle } from './actions.js';
479+
480+
export default function EditForm() {
481+
const [title, setTitle] = useState('My draft');
482+
483+
async function saveAction(formData) {
484+
const savedTitle = await submitTitle(formData);
485+
setTitle(savedTitle);
486+
}
487+
488+
return (
489+
<form action={saveAction}>
490+
<input name="title" defaultValue={title} />
491+
<button type="submit">Save</button>
492+
</form>
493+
);
494+
}
495+
```
496+
429497
<DeepDive>
430498

431-
#### Choosing how to manage form values {/*choosing-how-to-manage-form-values*/}
499+
#### Other ways to control form resets {/*other-ways-to-control-form-resets*/}
432500

433-
Choose an approach based on what should happen after submission:
501+
The examples above keep every submitted value by storing it before the reset. You can also choose which values survive, or when the reset runs:
434502

435-
* **Preserve selected values with `useActionState`.** The example above returns the submitted title after every submission. To preserve values only when validation fails, return the submitted `FormData` in the error state and use it to set each field's `defaultValue`. With a Server Function, React can include those values in the server response before JavaScript loads.
503+
* **Keep values only when validation fails.** Return the same state shape from every submission. When validation fails, include the submitted `FormData` and read each field with `state.submitted?.get('title') ?? ''`. When validation succeeds, set that `FormData` to `null` so the reset clears those fields. [`useActionState`](/reference/react/useActionState#displaying-validation-errors-and-preserving-form-values) shows this pattern.
436504

437-
* **Keep every value with `onSubmit`.** Call `e.preventDefault()`, then run the Action inside [`startTransition`](/reference/react/useTransition). Calling `preventDefault()` prevents the function passed to the form's `action` prop from running for that submission, so React does not automatically reset the form.
505+
* **Skip the reset with `onSubmit`.** Handle the submission in `onSubmit`, call `e.preventDefault()`, and run the Action inside [`startTransition`](/reference/react/useTransition). Because the form's `action` does not run, React does not reset the form. Users can submit this form only after JavaScript loads.
438506

439-
* **Reset fields at a specific point.** Call the form element's [`reset()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset) method to immediately reset uncontrolled fields to their default values. To schedule the same reset inside an Action or Transition, call [`requestFormReset`](/blog/2024/12/05/react-19#form-actions) from `react-dom`.
507+
* **Reset at a specific moment.** Call the form element's [`reset()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset) method to immediately reset uncontrolled fields to their default values. To schedule a reset as part of an Action or Transition, call [`requestFormReset`](/blog/2024/12/05/react-19#form-actions) from `react-dom` instead.
440508

441-
* **Reset the fields and component state.** Change the [`key`](/learn/preserving-and-resetting-state#resetting-a-form-with-a-key) on the component that renders the form. React recreates the component and its DOM, so its fields and local state both start over.
509+
* **Reset the form and the component state together.** Change the [`key`](/learn/preserving-and-resetting-state#resetting-a-form-with-a-key) of the component that renders the form. React recreates the component and its DOM, which resets the fields and any local state.
442510

443511
</DeepDive>
444512

0 commit comments

Comments
 (0)