Capítulo 48 de 116

Chapter 48: useActionState

Core Idea

useActionState combines a reducer-style state update with a pending flag specifically for Actions (async functions triggered by form submission or a transition), so a form's pending/result state doesn't need to be hand-wired with separate useState calls.

Key Concepts

  • Signature: const [state, dispatchAction, isPending] = useActionState(reducerAction, initialState, permalink?).
  • reducerAction(previousState, actionPayload): called each time the returned dispatchAction fires; receives the previous state (or initialState on the very first call) plus whatever payload was dispatched, and returns the new state.
  • Three return values: state (current, starts as initialState), dispatchAction (call this from a form's action prop or an event handler to trigger the update — has a stable identity across renders), isPending (true while a dispatched Action is in flight).
  • Sequential queueing: multiple dispatchAction calls are queued and run in order, each reducerAction call receiving the previous call's result — never run concurrently against each other.
  • permalink (optional): for React Server Components progressive enhancement — if the form is submitted before the JS bundle loads, the browser navigates to this URL instead of the current page, so the same component (with matching reducerAction/permalink) must render on the destination page too.
  • Must be called at the top level, like any Hook — no conditions or loops.

Code Examples

function reducerAction(previousState, payload) {
  return { ...previousState, count: previousState.count + payload.amount };
}

function Cart({ initialState }) {
  const [state, dispatchAction, isPending] = useActionState(reducerAction, initialState);
  return (
    <form action={() => dispatchAction({ amount: 1 })}>
      <button disabled={isPending}>Add ({state.count})</button>
    </form>
  );
}
  • What it demonstrates: wiring a form's action directly to dispatchAction, with isPending disabling the button while the Action is in flight — no separate useState for the pending flag.

Key Takeaways

  1. Use this instead of a plain useReducer + manual isPending state whenever the state update is driven by a form Action or transition-triggered function.
  2. dispatchAction's stable identity means it's safe to omit from Effect dependency arrays without causing stale closures.
  3. permalink only matters for RSC progressive-enhancement forms — most client-only forms can ignore it entirely.

Connects To

  • Ch 65 (useTransition): the general Actions concept this Hook specializes for reducer-style state.
  • Ch 31 (Extracting State Logic into a Reducer): the reducer pattern this Hook is built on.