eduardweb.
Hooks & PatternsIntermediar#react#frontend#react-19#javascript

React 19 useOptimistic: UI instant și rollback fără dureri de cap

De Adrian Voicu, 2 aug. 2026 · 4 vizualizări · 2 like-uri

Postat 2 aug. 2026
tsx
import { useOptimistic } from 'react';

type Comment = { id: string; text: string; pending?: boolean };

export function CommentSection({ comments, addCommentAction }: {
  comments: Comment[];
  addCommentAction: (formData: FormData) => Promise<void>;
}) {
  const [optimisticComments, addOptimistic] = useOptimistic(
    comments,
    (current, newText: string) => [
      ...current,
      { id: crypto.randomUUID(), text: newText, pending: true }
    ]
  );

  async function handleSubmit(formData: FormData) {
    const text = formData.get('comment') as string;
    if (!text.trim()) return;
    
    addOptimistic(text);
    await addCommentAction(formData);
  }

  return (
    <section>
      {optimisticComments.map((c) => (
        <div key={c.id} style={{ opacity: c.pending ? 0.5 : 1 }}>
          <p>{c.text} {c.pending && <small>(se trimite...)</small>}</p>
        </div>
      ))}
      <form action={handleSubmit}>
        <input name="comment" required />
        <button type="submit">Adaugă</button>
      </form>
    </section>
  );
}

Dacă ai făcut vreodată update-uri optimiste manual în React folosind useState combinat cu useEffect, știi ce coșmar e să gestionezi starea când request-ul de rețea crapă. În React 19, hook-ul useOptimistic rezolvă exact problema asta, eliminând starea temporară duplicată. Am refăcut recent secțiunea de comentarii pe un proiect cu peste 12k utilizatori activi lunar și am șters vreo 80 de linii de boilerplate greu de întreținut.

Ce problemă rezolvă mai exact?

Când un utilizator apasă pe „Trimite comentariu”, se așteaptă să vadă textul pe ecran instantaneu, nu după 400ms când se termină roundtrip-ul până la baza de date. În abordarea clasică, trebuia să adaugi un element provizoriu în state, să faci apelul de API, iar pe blocul catch să cauți elementul după un ID generat local și să-l elimini.

useOptimistic primește starea reală (sursa de adevăr) și îți oferă o stare derivată pe care o poți modifica temporar pe durata executării unei acțiuni asincrone. De îndată ce acțiunea de fundal se finalizează — fie că s-a terminat cu succes sau s-a aruncat o eroare — React abandonează starea optimistă și revine automat la starea de adevăr sincronizată de server.

Cum funcționează cu Server Actions

Hook-ul primește două argumente principale: starea curentă transmisă din props sau dintr-un state superior și o funcție de reducer care definește cum se îmbină noua valoare optimistă cu starea existentă.

În timpul trimiterii formularului, apelezi funcția de update optimist înainte să finalizezi apelul asincron. Componenta se va reranda instant cu elementul nou în stare semi-transparentă (sau cu un indicator vizual de încărcare). Dacă serverul răspunde cu eroare, React resetează starea la cea dinaintea apelului, fără să fie nevoie să scrii vreun bloc manual de curățare.

Compromisuri și capcane în producție

Deși mecanismul este excelent pentru UX, am observat două probleme la utilizarea în aplicații reale:

  1. Flicker vizual la structuri complexe: Dacă elementul temporar generat de tine în frontend arată diferit ca structură față de ce returnează backend-ul (de exemplu, backend-ul sanitizează HTML-ul sau adaugă un avatar populat din DB), utilizatorul va vedea un mic salt vizual când starea optimistă este înlocuită de datele reale.
  2. Pierderea datelor la eroare de rețea: Când request-ul pică, rollback-ul funcționează impecabil și șterge comentariul temporar. Totuși, dacă formularul s-a golit deja prin form.reset(), utilizatorul își pierde textul scris. În producție, este obligatoriu să combini useOptimistic cu o salvare locală a textului până la confirmarea finală sau cu o notificare clară de tip Toast care să-i permită refacerea acțiunii.

Voi ce soluții folosiți pentru gestionarea stărilor optimiste în proiectele mari, vă bazați pe React 19 sau preferați în continuare abordarea din React Query / TanStack Mutate?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.