The historical over-reliance on client stores

For nearly a decade, the standard React blueprint mandated putting virtually every piece of fetched data into global stores like Redux, MobX, or complicated context providers. This resulted in thousands of lines of boilerplate actions, reducers, and selectors.

In reality, over 80% of what developers stored in global state was simply a cached copy of server data. Treating server responses as local client state created persistent cache synchronization headaches.

React 19 native action state and transitions

With React 19 and modern framework paradigms, asynchronous form submissions, pending indicators, and optimistic mutations are native primitives. Hooks like useActionState and useOptimistic eliminate the need for third-party form libraries and state machines for typical web interactions.

ModernFormState.tsxtypescript
'use client';
import { useActionState } from 'react';

async function updateProfileName(prevState: unknown, formData: FormData) {
  const name = formData.get('name') as string;
  const res = await fetch('/api/profile', {
    method: 'POST',
    body: JSON.stringify({ name }),
  });
  return res.json();
}

export function ProfileForm() {
  const [state, formAction, isPending] = useActionState(updateProfileName, null);

  return (
    <form action={formAction}>
      <input name="name" disabled={isPending} />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Saving changes...' : 'Save'}
      </button>
    </form>
  );
}