import { useOptimistic, useTransition } from "react";
type Comment = { id: string; text: string; sending?: boolean };
export function CommentSection({
initialComments,
addCommentAction,
}: {
initialComments: Comment[];
addCommentAction: (text: string) => Promise<Comment>;
}) {
const [isPending, startTransition] = useTransition();
const [optimisticComments, setOptimisticComments] = useOptimistic(
initialComments,
(state: Comment[], newCommentText: string) => [
...state,
{ id: Math.random().toString(), text: newCommentText, sending: true },
]
);
const handleFormSubmit = async (formData: FormData) => {
const text = formData.get("comment") as string;
if (!text.trim()) return;
startTransition(async () => {
setOptimisticComments(text);
try {
await addCommentAction(text);
} catch (error) {
console.error("Salvarea a eșuat, rollback automat realizat de React.");
}
});
};
return (
<div className="space-y-4">
<form action={handleFormSubmit} className="flex gap-2">
<input
type="text"
name="comment"
className="border p-2 rounded"
placeholder="Scrie un comentariu..."
/>
<button type="submit" disabled={isPending} className="bg-blue-500 text-white p-2">
Trimite
</button>
</form>
<ul className="space-y-2">
{optimisticComments.map((comment) => (
<li
key={comment.id}
className={`p-2 rounded bg-gray-100 ${comment.sending ? "opacity-50 italic" : ""}`}
>
{comment.text} {comment.sending && " (se trimite...)"}
</li>
))}
</ul>
</div>
);
}Am testat useOptimistic din React 19 acum o lună pe un modul de comentarii pentru o platformă cu vreo 12.000 de utilizatori activi. Până acum făceam treaba asta clasic, cu stări locale de loading, dar codul devenea rapid o ciorbă greu de întreținut. Cu noul hook, utilizatorul vede comentariul instant pe ecran. Dacă pică rețeaua sau crapă serverul, React face rollback automat, curat, fără să lase UI-ul într-o stare ciudată.
De ce aveam nevoie de asta?
Înainte, ca să oferim o experiență rapidă, modificam manual starea globală sau locală înainte de a primi răspunsul de la API. Sună simplu la prima vedere. Dar când adaugi validări de server, latență fluctuantă și cazuri de eroare, codul se umplea de flag-uri de tip isSaving, errorSaving și blocuri de try/catch care trebuiau să curețe stările temporare.
useOptimistic rezolvă fix această problemă de sincronizare. Îți permite să randezi o stare temporară cât timp o acțiune asincronă (cum ar fi o Server Action) este în desfășurare. Când promisiunea se rezolvă, React revine automat la starea reală venită de pe server.
Cum funcționează concret
Hai să ne uităm pe un exemplu simplu pe care l-am extras din producție. Presupunem că avem o componentă care primește lista de comentarii deja salvate și o funcție de acțiune pentru salvare.
Hook-ul primește două argumente: starea curentă (pe care o folosește ca bază) și o funcție de update care decide cum se îmbină starea veche cu datele noi, temporare. În codul de mai jos, adăugăm imediat comentariul în listă cu un flag sending: true, ca să-l putem randa cu o opacitate redusă.
Trade-off-uri pe care le-am simțit pe pielea mea
Nu totul e perfect cu acest hook, iar cine spune asta probabil nu l-a pus în producție. Merge brici pentru adăugări simple, like-uri sau toggle-uri de status. În schimb, devine destul de enervant dacă datele returnate de server diferă mult ca structură de cele pe care le-ai generat tu "optimist" pe client.
De exemplu, dacă serverul generează un ID pe care tu nu ai cum să-l ghicești pe client, iar imediat după adăugare userul vrea să dea delete la acel comentariu proaspăt, ai o problemă. Până nu se termină request-ul de salvare și nu primești ID-ul real, butonul de delete nu are pe ce ID să facă trigger.
De asemenea, ține minte că useOptimistic funcționează doar în interiorul unei tranziții React (cum e cea oferită implicit de Server Actions în <form> sau prin startTransition). Dacă încerci să-l apelezi într-un simplu handler de click fără tranziție, pur și simplu nu se va întâmpla nimic.
Voi cum gestionați stările astea optimiste în aplicațiile voastre? Ați trecut deja pe React 19 sau încă așteptați să se mai așeze praful pe versiunile stabile?