Sitemap
shahrukh akhter

shahrukh akhter

React 19 Hooks Deep Dive — Real Scenarios & Practical Examples

4 min readFeb 15, 2026

--

React 19 is one of the biggest updates in the library’s history. Released on December 5, 2024, it ushers in powerful new patterns for asynchronous UI, server integration, performance optimization, and developer experience improvements. With features like Actions, new Hooks, Server Components, and a built-in compiler, this release makes everyday coding simpler and apps faster

The biggest shift?

👉 Async state management is now first-class.

In this article, we’ll deeply explore:

  • ✅ useActionState
  • ✅ useOptimistic
  • ✅ useFormStatus
  • ✅ use hook
  • ✅ Real-world patterns combining them

1️⃣ useActionState — The Future of Form State

🔥 Problem Before React 19

Every form required:

  • loading state
  • error state
  • success state
  • manual resets

Example (Old Way):

function LoginForm() {
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
setError(null);

try {
await loginApi(email);
} catch (err) {
setError(err.message);
}

setLoading(false);
}

return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} />
<button disabled={loading}>
{loading ? "Logging in..." : "Login"}
</button>
{error && <p>{error}</p>}
</form>
);
}

Too much boilerplate.

✅ React 19 Way — useActionState

import { useActionState } from "react";

async function loginAction(prevState, formData) {
const email = formData.get("email");

try {
await loginApi(email);
return { success: true };
} catch (err) {
return { error: err.message };
}
}

function LoginForm() {
const [state, formAction, isPending] =
useActionState(loginAction, {});

return (
<form action={formAction}>
<input name="email" />
<button disabled={isPending}>
{isPending ? "Logging in..." : "Login"}
</button>

{state?.error && <p>{state.error}</p>}
{state?.success && <p>Welcome!</p>}
</form>
);
}

🧠 What Just Happened?

  • No useState
  • No manual loading toggle
  • No event.preventDefault()
  • Native form action
  • Cleaner async workflow

This feels closer to backend frameworks — but fully reactive.

2️⃣ useOptimistic — Instant UI Without Waiting for Server

🔥 Real Scenario: Chat App

User sends a message.

We don’t want to wait for API.
We want instant UI update.

❌ Old Way

  • Add message to local state
  • If API fails → rollback manually

Messy.

✅ React 19 Way

import { useOptimistic } from "react";

function Chat({ messages, sendMessage }) {
const [optimisticMessages, addOptimisticMessage] =
useOptimistic(messages, (state, newMessage) => [
...state,
{ text: newMessage, sending: true }
]);

async function handleSend(formData) {
const message = formData.get("message");

addOptimisticMessage(message);

await sendMessage(message);
}

return (
<form action={handleSend}>
<input name="message" />
<button>Send</button>

{optimisticMessages.map((msg, i) => (
<div key={i}>
{msg.text} {msg.sending && "⏳"}
</div>
))}
</form>
);
}

💡 Why This Is Powerful

  • UI updates instantly
  • React tracks optimistic state
  • When server resolves → React reconciles

No manual rollback complexity.

This is HUGE for:

  • Comments
  • Likes
  • E-commerce cart
  • Social feeds
  • Reactions

3️⃣ useFormStatus — Deeply Nested Button Loading State

🔥 Problem

Form button is deep inside component tree.

Get shahrukh akhter’s stories in your inbox

Join Medium for free to get updates from this writer.

You don’t want prop drilling isLoading.

import { useFormStatus } from "react-dom";

function SubmitButton() {
const { pending } = useFormStatus();

return (
<button disabled={pending}>
{pending ? "Saving..." : "Save"}
</button>
);
}

function ProfileForm({ saveAction }) {
return (
<form action={saveAction}>
<input name="username" />
<SubmitButton />
</form>
);
}

🧠 What’s Beautiful Here?

  • Button automatically knows form state
  • No props
  • No context
  • No lifting state

It subscribes directly to parent form.

4️⃣ The use() Hook — Async Rendering Without Suspense Boilerplate

React 19 lets you directly read promises.

Scenario: Fetching Data

❌ Old Way

useEffect(() => {
fetchData().then(setData);
}, []);

Manual state.
Manual loading.
Manual error handling.

import { use } from "react";

function Products({ productsPromise }) {
const products = use(productsPromise);

return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
<Suspense fallback={<p>Loading...</p>}>
<Products productsPromise={fetchProducts()} />
</Suspense>

🧠 Why This Is Important

  • use() unwraps promises
  • Integrates naturally with Suspense
  • Cleaner data flow
  • Better concurrent rendering

This is foundational for React’s server-first future

5️⃣ Combining Hooks — Real E-commerce Example

Let’s combine:

  • useActionState
  • useOptimistic
  • useFormStatus
function AddToCart({ product }) {
const [optimisticCart, addOptimistic] =
useOptimistic([], (cart, item) => [...cart, item]);

async function addToCartAction(prev, formData) {
const id = formData.get("productId");

addOptimistic({ id, name: product.name });

await apiAddToCart(id);

return {};
}

const [state, formAction] =
useActionState(addToCartAction, {});

return (
<form action={formAction}>
<input type="hidden" name="productId" value={product.id} />
<SubmitButton />
</form>
);
}

What We Achieved

✔ Instant UI update
✔ Automatic loading state
✔ Cleaner server mutation
✔ Zero prop drilling
✔ Minimal boilerplate

This is modern React architecture.

6️⃣ Mental Model Shift in React 19

Old React mindset:

Event -> setState -> render -> effect -> fetch -> setState again

React 19 mindset:

Action -> async logic -> React handles pending -> UI updates declaratively

It feels more like:

  • Remix
  • Next.js server actions
  • Backend frameworks
  • Declarative async workflows

⚡ Why This Matters for Senior Developers

If you’re 8+ years into React (like you 😉), this is important because:

  • Boilerplate reduction is massive
  • Async flows are first-class citizens
  • Server + Client boundary is cleaner
  • Better mental model for scalable apps
  • Less useEffect misuse

React is moving toward:

👉 Server-first
👉 Suspense-driven
👉 Action-driven async UI
👉 Compiler-optimized components

🚀 Final Thoughts

React 19 is not just “new hooks”.

It’s a philosophical shift toward:

  • Declarative async UI
  • Native form actions
  • Optimistic rendering
  • Better server integration
  • Cleaner architecture

--

--