diff --git a/src/content/blog/2024/04/01/react-19.md b/src/content/blog/2024/04/01/react-19.md index bb2c94db5..7844f6809 100644 --- a/src/content/blog/2024/04/01/react-19.md +++ b/src/content/blog/2024/04/01/react-19.md @@ -30,24 +30,35 @@ For a list of breaking changes, see the [Upgrade Guide](/blog/2024/04/01/react-1 A common use case in React apps is to perform a data mutation and then update state in response. For example, when a user submits a form to change their name, you will make an API request, and then handle the response. In the past, you would need to handle pending states, errors, optimistic updates, and sequential requests manually. -For example, you could handle the pending state in `useState`: +For example, you could handle the pending and error state in `useState`: -```js {5,8,10} -const [name, setName] = useState(''); -const [error, setError] = useState(null); +```js +// Before Actions +function UpdateName({}) { + const [name, setName] = useState(""); + const [error, setError] = useState(null); + const [isPending, setIsPending] = useState(false); -// Manually handle the pending state -const [isPending, setIsPending] = useState(false); + const handleSubmit = async () => { + setIsPending(true); + const error = await updateName(name); + setIsPending(false); + if (error) { + setError(error); + return; + } + redirect("/path"); + }; -const handleSubmit = async () => { - setIsPending(true); - const {error} = await updateName(name); - setIsPending(false); - if (error) { - setError(error); - } else { - setName(''); - } + return ( +
+ setName(event.target.value)} /> + + {error &&

{error}

} +
+ ); } ``` @@ -55,27 +66,37 @@ In React 19, we're adding support for using async functions in transitions to ha For example, you can use `useTransition` to handle the pending state for you: -```js {5,8,15} -const [name, setName] = useState(''); -const [error, setError] = useState(null); +```js +// Using pending state from Actions +function UpdateName({}) { + const [name, setName] = useState(""); + const [error, setError] = useState(null); + const [isPending, startTransition] = useTransition(); -// Pending state is handled for you -const [isPending, startTransition] = useTransition(); + const handleSubmit = async () => { + startTransition(async () => { + const error = await updateName(name); + if (error) { + setError(error); + return; + } + redirect("/path"); + }) + }; -const submitAction = async () => { - startTransition(async () => { - const {error} = await updateName(name); - if (!error) { - setError(error); - } else { - setName(''); - } - }) + return ( +
+ setName(event.target.value)} /> + + {error &&

{error}

} +
+ ); } ``` - -The async transition will immediately set the `isPending` state to true, make the async request(s), and render any state updates as transitions. This allows you to keep the current UI responsive and interactive while the data is changing. +The async transition will immediately set the `isPending` state to true, make the async request(s), and switch `isPending` to false after any transitions. This allows you to keep the current UI responsive and interactive while the data is changing. @@ -90,23 +111,49 @@ Actions automatically manage submitting data for you: -Async transitions are the raw primitive that power Actions, and you can always drop down to `useTransition`, `useState`, and `useOptimistic` to create your own custom behavior. We're also introducing the [`useActionState`](#new-hook-useactionstate) and [`useFormStatus`](#new-hook-useformstatus) hooks to support the common cases for Actions and Forms. +Building on top of Actions, we're also introducing [`
` Actions](#form-actions) to manage forms automatically, [`useOptimistic`](#new-hook-optimistic-updates) to manage optimistic updates, and new hooks [`useActionState`](#new-hook-useactionstate), [`useFormStatus`](#new-hook-useformstatus) hooks to support the common cases for Actions and Forms. -For more information, see the docs for [`useTransition`](/reference/react/useTransition) and the next sections. +In React 19, the above example can be simplified to: + +```js +// Using Actions and useActionState +function ChangeName({ name, setName }) { + const [error, submitAction, isPending] = useActionState( + async (previousState, formData) => { + const error = await updateName(formData.get("name")); + if (error) { + return error; + } + redirect("/path"); + } + ); + + return ( + + + + {error &&

{error}

} +
+ ); +} +``` + +In the next section, we'll break down each of the new Action features in React 19. ### New Hook: `useActionState` {/*new-hook-useactionstate*/} To make the common cases easier for Actions, we've added a new hook called `useActionState`: -```js {2,9} -const [name, setName] = useState(''); -const [error, submitAction, isPending] = useActionState(async () => { - const {error} = await updateName(name); - setName(''); +```js +const [error, submitAction, isPending] = useActionState(async (previousState, newName) => { + const {error} = await updateName(newName); + if (!error) { + // You can return any result of the action. + // Here, we return only the error. + return error; + } - // You can return any result of the action. - // Here, we return only the error. - return error; + // handle success }); ``` @@ -126,17 +173,8 @@ For more information, see the docs for [`useActionState`](/reference/react/useAc Actions are also integrated with React 19's new `
` features. We've added support for passing functions as the `action` and `formAction` props of ``, ``, and `