eduardweb.
Hooks & PatternsIntermediar#frontend#javascript#useoptimistic#react19

useOptimistic în React 19: Cum am scăpat de spinner-e la comentarii

De Andreea Crăciun, 4 aug. 2026 · 7 vizualizări · 2 like-uri

Postat 4 aug. 2026
typescript
import { useOptimistic } from "react";

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

export function CommentSection({ initialComments, addCommentAction }: {
  initialComments: Comment[];
  addCommentAction: (formData: FormData) => Promise<void>;
}) {
  const [optimisticComments, addOptimisticComment] = useOptimistic(
    initialComments,
    (state, newText: string) => [
      ...state,
      { id: Math.random().toString(), text: newText, sending: true }
    ]
  );

  async function handleSubmit(formData: FormData) {
    const text = formData.get("comment") as string;
    if (!text?.trim()) return;

    addOptimisticComment(text);
    await addCommentAction(formData);
  }

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

Salutare tuturor. Am trecut recent o platformă internă (în jur de 14k de utilizatori activi) pe React 19 și primul hook care mi-a rezolvat o frustrare veche a fost useOptimistic. În secțiunea asta vă arăt cum am eliminat complet spinner-ul de încărcare din formularul de comentarii, făcând interfața să se simtă instantanee, fără să scriu zeci de linii de rollback manual.

De la stări complicate la update instant

Până acum, când făceam un update optimist (de exemplu, adăugarea unui comentariu), trebuia să fac o jonglerie obositoare. Salvam starea veche într-o variabilă, adăugam comentariul cu un ID temporar în state-ul local, făceam request-ul de fetch sau axios, iar dacă serverul întorcea o eroare 500, făceam un set state în catch ca să revin la ce era înainte. Dacă uitai un singur caz de eroare, userul rămânea cu un comentariu „fantomă” pe ecran care nu exista în baza de date.

useOptimistic schimbă ecuația pentru că se leagă direct de tranzițiile din React și de Form Actions. Îi dai o stare curentă și o funcție de reducer/update. În momentul în care declanșezi acțiunea, React afișează imediat noua stare optimistă. Dacă action-ul async se rezolvă cu succes sau aruncă o eroare, React resetează automat starea optimistă și afișează starea reală sincronizată din server. Zero logică de rollback scrisă de mână în blocurile try/catch.

Cum funcționează în practică

Hai să vedem codul din aplicație. Avem o listă de comentarii venită din server și o acțiune asincronă care trimite comentariul nou prin API.

Când userul apasă Submit, addOptimisticComment rulează sincron înainte ca addCommentServerAction să finalizeze request-ul de rețea. UI-ul randază noul comentariu instant cu un opacity redus și mesajul „se trimite...”. Cât timp request-ul e pe drum (să zicem că durează 400ms), interfața răspunde impecabil. Dacă serverul dă fail (de exemplu o eroare 422 de validare sau o întrerupere de rețea), acțiunea dă throw, iar React face automat revert la lista inițială de comentarii. Fără efecte secundare rămase agățate în UI.

Trade-off-uri și capcane în producție

Nu e totul perfect, așa că e bine să știți la ce să vă așteptați înainte să aruncați peste tot cu noul hook:

  • Gestionarea cheilor temporare: Când adaugi un element optimist, ești obligat să-i generezi un key temporar (ex: Math.random()). Când vine răspunsul real de la server cu UUID-ul din DB, componentele copil își pierd starea internă dacă React nu potrivește elementele corect, rezultând într-un flicker vizibil.
  • Nu înlocuiește starea globală: useOptimistic e gândit strict pentru UI provizoriu asociat unei acțiuni în desfășurare. Dacă vrei stări optimiste care să persiste între navigări de pagini sau componente total deconectate, tot ai nevoie de un client-side store precum TanStack Query sau Zustand.
  • Debugging greoi la acțiuni rapide: În scenarii cu acțiuni concurente (userul dă click repetat de 5 ori foarte rapid), stack-ul de tranziții din React 19 devine destul de greu de inspectat în React DevTools.

La un latency obișnuit de 200-300ms pe rețea, utilizatorii percep aplicația ca fiind vizibil mai rapidă. Pe formularele noastre am măsurat o scădere a ratei de abandon cu 12% după ce am scos stările de loading.

Voi ați apucat să folosiți useOptimistic în producție pe React 19 sau încă așteptați să se stabilizeze ecosistemul?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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