-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Update form documentation for forms default reset behavior #8512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 9 commits
5245923
e426aa5
93f421c
7795146
2925e1d
fad1a7d
ffb4824
d6b03fa
0cf237a
b341728
4f39d4b
947c926
5a9e230
2cb1370
a3e4a9f
a3cd240
cfb4422
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,27 +42,23 @@ To create interactive controls for submitting information, render the [built-in | |
|
|
||
| #### Caveats {/*caveats*/} | ||
|
|
||
| * When a function is passed to `action` or `formAction` the HTTP method will be POST regardless of value of the `method` prop. | ||
| * When a function is passed to `action` or `formAction` the HTTP method will be POST regardless of the value of the `method` prop. | ||
| * 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 [Preserving form values after submission](#preserve-form-values-after-submission). | ||
|
|
||
| --- | ||
|
|
||
| ## Usage {/*usage*/} | ||
|
|
||
| ### Handle form submission with an event handler {/*handle-form-submission-with-an-event-handler*/} | ||
| ### 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. | ||
|
|
||
| This example reads the submitted values with [`new FormData(e.target)`](https://developer.mozilla.org/en-US/docs/Web/API/FormData), which collects every field by its `name`. This keeps the inputs [uncontrolled](/reference/react-dom/components/input#reading-the-input-values-when-submitting-a-form). If you instead [control an input with state](/reference/react-dom/components/input#controlling-an-input-with-a-state-variable), read from that state on submit rather than from `FormData`. | ||
|
|
||
| <Sandpack> | ||
|
|
||
| ```js src/App.js | ||
| export default function Search() { | ||
| function handleSubmit(e) { | ||
| // Prevent the browser from reloading the page | ||
| e.preventDefault(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed 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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm yeah i wasn't sure why this was removed either. I don't really have strong feelings either way tho |
||
|
|
||
| // Read the form data | ||
| const form = e.target; | ||
| const formData = new FormData(form); | ||
| const query = formData.get("query"); | ||
|
|
@@ -86,9 +82,22 @@ Reading form data with `onSubmit` works in every version of React and gives you | |
|
|
||
| </Note> | ||
|
|
||
| ### Handle form submission with an action prop {/*handle-form-submission-with-an-action-prop*/} | ||
| ### Handling form submission with an action prop {/*handle-form-submission-with-an-action-prop*/} | ||
|
|
||
| Pass a function to the `action` prop of `<form>` to handle submission. When the form is submitted, React calls your function with a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object containing the field values. | ||
|
|
||
| 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. | ||
|
MaxwellCohen marked this conversation as resolved.
Outdated
|
||
|
|
||
| When you pass a function to `action`, React: | ||
|
|
||
| * 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. | ||
|
MaxwellCohen marked this conversation as resolved.
Outdated
|
||
| * Works with [`useActionState`](/reference/react/useActionState) and [`useOptimistic`](/reference/react/useOptimistic) for form state and optimistic UI. | ||
|
MaxwellCohen marked this conversation as resolved.
Outdated
|
||
|
|
||
| After the 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 [Preserving form values after submission](#preserve-form-values-after-submission). | ||
|
|
||
| <Sandpack> | ||
|
|
||
|
|
@@ -109,11 +118,12 @@ export default function Search() { | |
|
|
||
| </Sandpack> | ||
|
|
||
| ### Handle form submission with a Server Function {/*handle-form-submission-with-a-server-function*/} | ||
|
|
||
| ### Handling form submission with a Server Function {/*handle-form-submission-with-a-server-function*/} | ||
|
|
||
| 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. | ||
|
|
||
| Passing a Server Function to `<form action>` allow users to submit forms without JavaScript enabled or before the code has loaded. This is beneficial to users who have a slow connection, device, or have JavaScript disabled and is similar to the way forms work when a URL is passed to the `action` prop. | ||
| Passing a Server Function to `<form action>` allows users to submit forms without JavaScript enabled or before the code has loaded. This is beneficial to users who have a slow connection, device, or have JavaScript disabled and is similar to the way forms work when a URL is passed to the `action` prop. | ||
|
|
||
| You can use hidden form fields to provide data to the `<form>`'s action. The Server Function will be called with the hidden form field data as an instance of [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData). | ||
|
|
||
|
|
@@ -157,7 +167,87 @@ function AddToCart({productId}) { | |
|
|
||
| When `<form>` is rendered by a [Server Component](/reference/rsc/use-client), and a [Server Function](/reference/rsc/server-functions) is passed to the `<form>`'s `action` prop, the form is [progressively enhanced](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). | ||
|
|
||
| ### 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. | ||
|
MaxwellCohen marked this conversation as resolved.
Outdated
|
||
|
|
||
| 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. | ||
|
|
||
| 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. | ||
|
|
||
| <Sandpack> | ||
|
|
||
| ```js src/App.js | ||
| import { useTransition } from "react"; | ||
| import { submitForm } from "./api.js"; | ||
|
|
||
| export default function EditForm() { | ||
| const [isPending, startTransition] = useTransition(); | ||
|
|
||
| function handleSubmit(e) { | ||
| // Stop React from resetting the form after the action succeeds | ||
| e.preventDefault(); | ||
| const formData = new FormData(e.target); | ||
| startTransition(async () => { | ||
| await submitForm(formData); | ||
| }); | ||
| } | ||
|
|
||
| return ( | ||
| <form action={submitForm} onSubmit={handleSubmit}> | ||
| <input name="title" defaultValue="My draft" /> | ||
| <button type="submit" disabled={isPending}> | ||
| {isPending ? "Saving..." : "Save"} | ||
| </button> | ||
| </form> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ```js src/api.js hidden | ||
| export async function submitForm(formData) { | ||
| await new Promise((res) => setTimeout(res, 1000)); | ||
| } | ||
| ``` | ||
|
|
||
| </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. | ||
|
MaxwellCohen marked this conversation as resolved.
Outdated
|
||
|
|
||
| <DeepDive> | ||
|
|
||
| #### Resetting only some fields, or resetting on the server {/*resetting-only-some-fields*/} | ||
|
|
||
| The `onSubmit` approach 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. | ||
|
|
||
| * **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: | ||
|
|
||
| ```js | ||
| import { useActionState } from "react"; | ||
| import { submitForm } from "./actions.js"; | ||
|
|
||
| function EditForm() { | ||
| // The action returns { submitted: formData, error } on failure | ||
| const [state, formAction] = useActionState(submitForm, {}); | ||
| const values = state.submitted ?? new FormData(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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/
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, I forgot about optional chaining (one of my favorite JS features when working on making the examples) |
||
| return ( | ||
| <form action={formAction}> | ||
| <input name="title" defaultValue={values.get("title") ?? ""} /> | ||
| {state.error && <p>{state.error}</p>} | ||
| <button type="submit">Save</button> | ||
| </form> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| Return the original `FormData` object rather than a new one so React can restore the values even before JavaScript has loaded. | ||
|
|
||
| </DeepDive> | ||
|
|
||
|
|
||
| ### Displaying a pending state during form submission {/*display-a-pending-state-during-form-submission*/} | ||
| To display a pending state when a form is being submitted, you can call the `useFormStatus` Hook in a component rendered in a `<form>` and read the `pending` property returned. | ||
|
|
||
| Here, we use the `pending` property to indicate the form is submitting. | ||
|
|
@@ -266,9 +356,7 @@ export async function deliverMessage(message) { | |
| ``` | ||
|
|
||
| </Sandpack> | ||
|
|
||
| [//]: # 'Uncomment the next line, and delete this line after the `useOptimistic` reference documentation page is published' | ||
| [//]: # 'To learn more about the `useOptimistic` Hook see the [reference documentation](/reference/react/useOptimistic).' | ||
| To learn more about the `useOptimistic` Hook see the [reference documentation](/reference/react/useOptimistic). | ||
|
|
||
| ### Handling form submission errors {/*handling-form-submission-errors*/} | ||
|
|
||
|
|
@@ -312,7 +400,7 @@ export default function Search() { | |
|
|
||
| </Sandpack> | ||
|
|
||
| ### Display a form submission error without JavaScript {/*display-a-form-submission-error-without-javascript*/} | ||
| ### Displaying a form submission error without JavaScript {/*display-a-form-submission-error-without-javascript*/} | ||
|
|
||
| Displaying a form submission error message before the JavaScript bundle loads for progressive enhancement requires that: | ||
|
|
||
|
|
@@ -372,28 +460,45 @@ Learn more about updating state from a form action with the [`useActionState`](/ | |
|
|
||
| ### Handling multiple submission types {/*handling-multiple-submission-types*/} | ||
|
|
||
| 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. | ||
| 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. | ||
|
|
||
| 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. | ||
|
aurorascharff marked this conversation as resolved.
Outdated
|
||
|
|
||
| <Sandpack> | ||
|
|
||
| ```js src/App.js | ||
| export default function Search() { | ||
| import { useState } from 'react'; | ||
|
|
||
| export default function ArticleForm() { | ||
| const [draft, setDraft] = useState(() => new FormData()); | ||
|
|
||
| function publish(formData) { | ||
| const content = formData.get("content"); | ||
| const button = formData.get("button"); | ||
| alert(`'${content}' was published with the '${button}' button`); | ||
| const content = formData.get('content'); | ||
| alert(`'${content}' was published!`); | ||
| // Clear the saved draft after publishing. | ||
| setDraft(new FormData()); | ||
| } | ||
|
|
||
| function save(formData) { | ||
| const content = formData.get("content"); | ||
| alert(`Your draft of '${content}' has been saved!`); | ||
| const content = formData.get('content'); | ||
| alert(`Your draft of '${content}' was saved!`); | ||
| // Keep the submitted content as the current draft. | ||
| setDraft(formData); | ||
| } | ||
|
|
||
| const savedContent = draft.get('content') || ''; | ||
| return ( | ||
| <form action={publish}> | ||
| <textarea name="content" rows={4} cols={40} /> | ||
| <textarea | ||
| // Changing the key resets the textarea to the saved draft. | ||
| key={`${savedContent}-content`} | ||
| name="content" | ||
| rows={4} | ||
| cols={40} | ||
| defaultValue={savedContent} | ||
| /> | ||
| <br /> | ||
| <button type="submit" name="button" value="submit">Publish</button> | ||
| <button formAction={save}>Save draft</button> | ||
|
|
@@ -402,4 +507,4 @@ export default function Search() { | |
| } | ||
| ``` | ||
|
|
||
| </Sandpack> | ||
| </Sandpack> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe change to
in your event handlerinstead of justhandlerthought its probably pretty obviousThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
upated in cfb4422