React 19 Hooks: use(), useActionState & useOptimistic

React 19 ships a set of hooks built for the async, form-driven UIs you actually build. use() reads the value out of a promise (or context) and plugs into Suspense, so you stop hand-writing loading/error/data branches. useActionState manages a form action's returned state plus a pending flag in one hook. useFormStatus lets a child button know the surrounding form is submitting. And useOptimistic shows the expected result instantly, then reconciles with reality. Together they turn fiddly async glue into a few lines. By the end you'll know what each unwraps, when to reach for it, and how they pair with Server Actions.

Learn React 19 Hooks: use(), useActionState & useOptimistic in our free React course — a beginner-friendly interactive lesson with runnable examples, a…

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

1️⃣ use(): Read Promises & Context

The use() hook unwraps a value. Pass it a promise and it returns the resolved data — suspending to the nearest while pending, and throwing to an error boundary if it rejects. The result: your component reads as if the data is simply there , with no loading/error branches in the body.

use() also reads context — and unlike every other hook, it can be called conditionally , inside an if or loop:

The demo models the three outcomes use() reacts to — pending suspends, fulfilled returns, rejected throws. Run it to lock in the mental model:

2️⃣ useActionState & useFormStatus: Forms Done Right

useActionState(action, initialState) returns [state, formAction, isPending] . Your action receives the previous state and the form data and returns the next state — perfect for validation errors or a success message. Wire formAction to the form and you get isPending for free.

When the submit button lives in its own reusable component, it can't see the parent's isPending . That's what useFormStatus is for — a child inside the reads the surrounding form's status directly:

Now model the heart of useActionState : the action function that takes the previous state plus form data and returns the next state. Fill in the blanks:

3️⃣ useOptimistic: Instant, Reconciled UI

Network calls take time, but users want feedback now . useOptimistic(state, updateFn) returns an optimisticState and an addOptimistic function. You show the expected result immediately, run the real action, and when it finishes React automatically drops the optimistic value and re-renders from the true state — so a failure just snaps back, no manual undo.

The demo models the show-then-reconcile timeline: the UI jumps to the optimistic value, then settles on the server's confirmed value. Run it:

Each scenario needs exactly one of these React 19 hooks. Finish pickHook so it returns the right name for each case — the decision you make in real code.

These lines of an optimistic like handler are scrambled. Put them in the correct order:

1) Why must the promise passed to use() be created outside render?

Creating it in the body makes a new promise every render, which Suspense can never settle — an infinite loading loop. Create it outside render (prop, cache, or a Server Component) and pass the stable promise in.

2) What three things does useActionState return?

[state, formAction, isPending] — the latest state your action returned, the action to put on , and a pending flag while it runs.

3) Your reusable needs to know the form is submitting. Which hook?

useFormStatus (from react-dom ). A child of the form reads {' '} directly — no prop drilling.

4) With useOptimistic , what happens if the real action fails?

React discards the optimistic value and re-renders from the real state — so the UI snaps back automatically. You never write manual undo logic.

📋 Quick Reference

Finish signupAction so it returns an error state for bad input and a message state on success — exactly the shape useActionState threads back into your form.

Practice quiz

What does the use() hook do with a promise?

  • Starts a new fetch
  • Caches the result forever
  • Reads (unwraps) the promise's value, suspending while pending
  • Converts it to state

Answer: Reads (unwraps) the promise's value, suspending while pending. use() unwraps a promise: it suspends to the nearest <Suspense> while pending and returns the resolved value.

Why must the promise passed to use() be created outside render?

  • Creating it in the body makes a new promise every render, which Suspense can never settle
  • It runs faster
  • Promises can't be created in components
  • To avoid TypeScript errors

Answer: Creating it in the body makes a new promise every render, which Suspense can never settle. A new promise per render causes an infinite loading loop; create it outside render and pass a stable one.

Unlike other hooks, use() may be called…

  • Only at the top level
  • Only in class components
  • Only once per app
  • Conditionally, inside an if or a loop

Answer: Conditionally, inside an if or a loop. use() is unusual: it's allowed inside conditionals and loops, unlike the rules-of-hooks for other hooks.

What does useActionState return?

useActionState(action, initialState) returns [state, formAction, isPending].

A useActionState action function receives…

  • The previous state and the form data, and returns the next state
  • Only the form data
  • Just the event object
  • The component's props

Answer: The previous state and the form data, and returns the next state. The action gets (prevState, formData) and returns the next state — ideal for validation errors or a success message.

Which hook lets a reusable child SubmitButton know the form is submitting?

  • useActionState
  • useOptimistic
  • useFormStatus
  • useState

Answer: useFormStatus. useFormStatus, read by a child inside the <form>, exposes { pending } without prop drilling.

useFormStatus is imported from…

  • react
  • react-dom
  • react-router-dom
  • @reduxjs/toolkit

Answer: react-dom. useFormStatus comes from react-dom and only works inside a <form>.

With useOptimistic, what happens if the real action fails?

  • You must manually undo the change
  • The UI stays on the optimistic value
  • It throws to an error boundary
  • React discards the optimistic value and re-renders from the real state

Answer: React discards the optimistic value and re-renders from the real state. The optimistic value is disposable; React drops it and shows the true state, so the UI snaps back automatically.

useFormStatus must be read…

  • In the same component that renders the <form>
  • In a child component inside the <form>
  • At the app root
  • Inside useEffect

Answer: In a child component inside the <form>. It reads the nearest enclosing form, so it belongs in a child, not the component that renders the form.

Around a component that calls use() on a promise, you should wrap…

  • Nothing extra is needed
  • A Redux Provider
  • A <Suspense> boundary and an error boundary
  • A useMemo

Answer: A <Suspense> boundary and an error boundary. Suspense catches the pending state and an error boundary catches rejection; both are needed.