Used to manage complex or interdependent state in function components, especially when useState becomes unscalable.

useReducer()

const [state, dispatch] = useReducer(reducer, initialArg, init?)

Manages state transitions through a centralized reducer function and dispatch() actions.

import { useReducer } from 'react';

function reducer(state, action) {
  if (action.type === 'incremented_age') {
    return {
      age: state.age + 1
    };
  }
  throw Error('Unknown action.');
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  return (
    <>
      <button onClick={() => {
        dispatch({ type: 'incremented_age' })
      }}>
        Increment age
      </button>
      <p>Hello! You are {state.age}.</p>
    </>
  );
}

Use when:

useActionState()

const [state, formAction, isPending] = useActionState(actionFn, initialState, permalink?)

Simplifies reducer-style logic with async support and built-in form submission handling.

"use client";

import { useRef } from "react";
import { useActionState } from "react";

type FormState = {
  success: boolean;
  title?: string;
  error?: string;
};

async function createPost(prevState: FormState, formData: FormData) {
  "use server";
  const title = formData.get("title");

  if (!title || typeof title !== "string" || title.trim() === "") {
    return { success: false, error: "Title is required." };
  }

  try {
    // await prisma.post.create({ data: { title } });
    return { success: true, title };
  } catch (error) {
    return { success: false, error: "Failed to create post." };
  }
}

export default function StatefulForm() {
  const formRef = useRef<HTMLFormElement>(null);

  const [state, formAction, isPending] = useActionState<FormState, FormData>(
    async (prevState, formData) => {
      const result = await createPost(prevState, formData);

      if (result.success) {
        formRef.current?.reset();
      }

      return result;
    },
    { success: false }
  );

  return (
    <form ref={formRef} action={formAction} className="space-y-4 max-w-md">
      <input name="title" placeholder="Post title" className="border px-3 py-2 rounded w-full" />

      <button
        type="submit"
        disabled={isPending}
        className="bg-blue-600 text-white px-4 py-2 rounded"
      >
        {isPending ? "Submitting..." : "Create Post"}
      </button>

      {state.success && <p className="text-green-600">✅ Created: {state.title}</p>}
      {state.error && <p className="text-red-500">⚠️ {state.error}</p>}
    </form>
  );
}

Features:

Use when: