diff --git a/src/content/reference/react-dom/hooks/useFormState.md b/src/content/reference/react-dom/hooks/useFormState.md new file mode 100644 index 00000000000..ca7af253611 --- /dev/null +++ b/src/content/reference/react-dom/hooks/useFormState.md @@ -0,0 +1,291 @@ +--- +title: useFormState +canary: true +--- + + + +The `useFormState` Hook is currently only available in React's canary and experimental channels. Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). In addition, you need to use a framework that supports React Server Components to get the full benefit of `useFormState`. + + + + + +`useFormState` is a Hook that allows you to read the return value of the form action after a form is submitted. + +```js +const [state, formAction] = useFormState(action, initalState); +``` + + + + + +--- + +## Reference {/*reference*/} + +### `useFormState()` {/*useformstate*/} + +In the context of React Server Components, an *action* is a function that may be [executed when a form is submitted](/reference/react-dom/components/form). You can execute actions on the server or on the client. + +{/* TODO T164397693: link to actions documentation once it exists */} + +Call `useFormState` at the top level of your component to see the return value of an action after submitting a form. You pass `useFormState` an existing action as well as an initial state, and it returns a new action that you use when submitting your form, along with the latest form state. + +```js +function AddToCart({itemID}) { + const [message, formAction] = useFormState(addToCartAction, null); + return ( +
+ +
+ {message} + + ); +} + +export default function App() { + return ( + <> + + + + ) +} +``` + +```js actions.js +"use server"; + +export async function addToCart(prevState, queryData) { + const itemID = queryData.get('itemID'); + if (itemID === "1") { + return "Added to cart"; + } else { + return "Couldn't add to cart: the item is sold out."; + } +} +``` + +```css styles.css hidden +form { + border: solid 1px black; + margin-bottom: 24px; + padding: 12px +} + +form button { + margin-right: 12px; +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "^5.0.0" + }, + "main": "/index.js", + "devDependencies": {} +} +``` + + + + +#### Display structured information after submitting a form {/*display-structured-information-after-submitting-a-form*/} + +The return value from a server action can be any serializable value. For example, it could be an object that includes a boolean indicating whether the action was successful, an error message, or updated information. + + + +```js App.js +import { useState } from "react"; +import { useFormState } from "react-dom"; +import { addToCart } from "./actions.js"; + +function AddToCartForm({itemID, itemTitle}) { + const [formState, formAction] = useFormState(addToCart, {}); + return ( +
+

{itemTitle}

+ + + {formState?.success && +
+ Added to cart! Your cart now has {formState.cartSize} items. +
+ } + {formState?.success === false && +
+ Failed to add to cart: {formState.message} +
+ } +
+ ); +} + +export default function App() { + return ( + <> + + + + ) +} +``` + +```js actions.js +"use server"; + +export async function addToCart(prevState, queryData) { + const itemID = queryData.get('itemID'); + if (itemID === "1") { + return { + success: true, + cartSize: 12, + }; + } else { + return { + success: false, + message: "The item is sold out.", + }; + } +} +``` + +```css styles.css hidden +form { + border: solid 1px black; + margin-bottom: 24px; + padding: 12px +} + +form button { + margin-right: 12px; +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "^5.0.0" + }, + "main": "/index.js", + "devDependencies": {} +} +``` +
+ + + + + +## Troubleshooting {/*troubleshooting*/} + +### My action can no longer read the submitted form data {/*my-action-can-no-longer-read-the-submitted-form-data*/} + +When you wrap an action with `useFormState`, it gets an extra argument *as its first argument*. The submitted form data is therefore its *second* argument instead of its first as it would usually be. The new first argument that gets added is the current state of the form. + +```js +function action(currentState, formData) { + // ... +} +``` diff --git a/src/sidebarReference.json b/src/sidebarReference.json index acc93328ace..33afbf89ea8 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -166,6 +166,11 @@ "title": "Hooks", "path": "/reference/react-dom/hooks", "routes": [ + { + "title": "useFormState", + "path": "/reference/react-dom/hooks/useFormState", + "canary": true + }, { "title": "useFormStatus", "path": "/reference/react-dom/hooks/useFormStatus",