From 90ca701621424ca6cb4b2753df600604e73f465f Mon Sep 17 00:00:00 2001 From: Matt Carroll <7158882+mattcarrollcode@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:33:17 -0700 Subject: [PATCH] Add `useFormStatus` Hook API reference documentation (#6314) Co-authored-by: Luna Wei --- .../reference/react-dom/hooks/index.md | 48 ++++ .../react-dom/hooks/useFormStatus.md | 260 ++++++++++++++++++ src/sidebarReference.json | 11 + 3 files changed, 319 insertions(+) create mode 100644 src/content/reference/react-dom/hooks/index.md create mode 100644 src/content/reference/react-dom/hooks/useFormStatus.md diff --git a/src/content/reference/react-dom/hooks/index.md b/src/content/reference/react-dom/hooks/index.md new file mode 100644 index 000000000..c7afa1189 --- /dev/null +++ b/src/content/reference/react-dom/hooks/index.md @@ -0,0 +1,48 @@ +--- +title: "React DOM Hooks" +--- + + + +The `react-dom` package contains Hooks that are only supported for web applications (which run in the browser DOM environment). These Hooks are not supported in non-browser environments like iOS, Android, or Windows applications. If you are looking for Hooks that are supported in web browsers *and other environments* see [the React Hooks page](/reference/react). This page lists all the Hooks in the `react-dom` packakge. + + + +--- + +## Form Hooks {/*form-hooks*/} + + + +Form Hooks are currently only available in React's canary and experimental channels. Learn more about [React's release channels here](/community/versioning-policy#all-release-channels). + + + +*Forms* let you create interactive controls for submitting information. To manage forms in your components, use one of these Hooks: + +* [`useFormStatus`](/reference/react-dom/hooks/useFormStatus) allows you to make updates to the UI based on the status of the a form. +* `useFormState` allows you to manage state inside a form. + +```js +function Form({ action }) { + async function increment(n) { + return n + 1; + } + const [count, incrementFormAction] = useFormState(increment, 0); + return ( +
+ +
+ ); +} +``` diff --git a/src/content/reference/react-dom/hooks/useFormStatus.md b/src/content/reference/react-dom/hooks/useFormStatus.md new file mode 100644 index 000000000..ec63ed31f --- /dev/null +++ b/src/content/reference/react-dom/hooks/useFormStatus.md @@ -0,0 +1,260 @@ +--- +title: useFormStatus +canary: true +--- + + + +The `useFormStatus` 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). + + + + + +`useFormStatus` is a Hook that gives you status information of the last form submission. + +```js +const { pending, data, method, action } = useFormStatus(); +``` + + + + + +--- + +## Reference {/*reference*/} + +### `useFormStatus()` {/*use-form-status*/} + +The `useFormStatus` Hook provides status information of the last form submission. + +```js {4},[[1, 5, "status.pending"]] +import action from './actions'; + +function Submit() { + const status = useFormStatus(); + return +} + +export default App() { + return ( +
+ + + ); +} +``` + +To get status information, the `Submit` component must be rendered within a `
`. The Hook returns information like the `pending` property which tells you if the form is actively submitting. + +In the above example, `Submit` uses this information to disable ` + ); +} + +function Form({ action }) { + return ( + + + + ); +} + +export default function App() { + return
; +} +``` + +```js actions.js hidden +export async function submitForm(query) { + await new Promise((res) => setTimeout(res, 1000)); +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "react-scripts": "^5.0.0" + }, + "main": "/index.js", + "devDependencies": {} +} +``` + + + + +##### `useFormStatus` will not return status information for a `` rendered in the same component. {/*useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component*/} + +The `useFormStatus` Hook only returns status information for a parent `` and not for any `` rendered in the same component calling the Hook, or child components. + +```js +function Form() { + // 🚩 `pending` will never be true + // useFormStatus does not track the form rendered in this component + const { pending } = useFormStatus(); + return ; +} +``` + +Instead call `useFormStatus` from inside a component that is located inside `
`. + +```js +function Submit() { + // ✅ `pending` will be derived from the form that wraps the Submit component + const { pending } = useFormStatus(); + return ; +} + +function Form() { + // This is the `useFormStatus` tracks + return ( + + + + ); +} +``` + +
+ +### Read the form data being submitted {/*read-form-data-being-submitted*/} + +You can use the `data` property of the status information returned from `useFormStatus` to display what data is being submitted by the user. + +Here, we have a form where users can request a username. We can use `useFormStatus` to display a temporary status message confirming what username they have requested. + + + +```js UsernameForm.js active +import {useState, useMemo, useRef} from 'react'; +import {useFormStatus} from 'react-dom'; + +export default function UsernameForm() { + const {pending, data} = useFormStatus(); + + const [showSubmitted, setShowSubmitted] = useState(false); + const submittedUsername = useRef(null); + const timeoutId = useRef(null); + + useMemo(() => { + if (pending) { + submittedUsername.current = data?.get('username'); + if (timeoutId.current != null) { + clearTimeout(timeoutId.current); + } + + timeoutId.current = setTimeout(() => { + timeoutId.current = null; + setShowSubmitted(false); + }, 2000); + setShowSubmitted(true); + } + }, [pending, data]); + + return ( + <> +
+ + + {showSubmitted ? ( +

Submitted request for username: {submittedUsername.current}

+ ) : null} + + ); +} +``` + +```js App.js +import UsernameForm from './UsernameForm'; +import { submitForm } from "./actions.js"; + +export default function App() { + return ( +
+ + + ); +} +``` + +```js actions.js hidden +export async function submitForm(query) { + await new Promise((res) => setTimeout(res, 1000)); +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "canary", + "react-dom": "canary", + "react-scripts": "^5.0.0" + }, + "main": "/index.js", + "devDependencies": {} +} +``` +
+ +--- + +## Troubleshooting {/*troubleshooting*/} + +### `status.pending` is never `true` {/*pending-is-never-true*/} + +`useFormStatus` will only return status information for a parent `
`. + +If the component that calls `useFormStatus` is not nested in a ``, `status.pending` will always return `false`. Verify `useFormStatus` is called in a component that is a child of a `` element. + +`useFormStatus` will not track the status of a `` rendered in the same component. See [Pitfall](#useformstatus-will-not-return-status-information-for-a-form-rendered-in-the-same-component) for more details. \ No newline at end of file diff --git a/src/sidebarReference.json b/src/sidebarReference.json index f9049956f..3c8daf71a 100644 --- a/src/sidebarReference.json +++ b/src/sidebarReference.json @@ -161,6 +161,17 @@ "hasSectionHeader": true, "sectionHeader": "react-dom@18.2.0" }, + { + "title": "Hooks", + "path": "/reference/react-dom/hooks", + "routes": [ + { + "title": "useFormStatus", + "path": "/reference/react-dom/hooks/useFormStatus", + "canary": true + } + ] + }, { "title": "Components", "path": "/reference/react-dom/components",