Server state și TanStack Query
De ce datele de pe server nu sunt useState, query keys, staleTime și invalidare.
Actualizat
Ce este server state
Server state = date care trăiesc pe server (în baza de date, într-un API), iar tu ai în browser doar o copie: useri, produse, comenzi, comentarii. Opusul e client state — date care există doar în UI: un modal deschis, textul dintr-un input, tema.
Diferența nu e academică — cele două au probleme complet diferite:
| Client state | Server state | |
|---|---|---|
| Exemple | tema, modal deschis, pas dintr-un wizard | useri, produse, comenzi |
| Proprietar | tu (UI-ul) | serverul — tu ai o copie |
| Sincron | da | nu — vine prin rețea |
| Se poate învechi | nu | oricând (alt user l-a modificat) |
| Probleme | unde îl ții | loading, erori, cache, deduplicare, refetch, invalidare, paginare |
| Unelte | useState, URL, Zustand |
Server Components, TanStack Query, SWR |
Greșeala clasică: date de server în useState + useEffect. Ajungi să reimplementezi — prost — cache, loading, retry, deduplicare.
Varianta 1: Server Components (Next.js)
Aduci datele pe server și randezi direct HTML-ul. Nu există loading state pe client, nu există cache de sincronizat în browser.
export default async function Page() {
const posts = await getPosts() // cache-uit cu 'use cache' + cacheTag('posts')
return <PostList posts={posts} />
}Mutație → Server Action → updateTag('posts') → pagina se re-randează cu date proaspete.
Varianta 2: TanStack Query (pe client)
O librărie care gestionează server state în browser: cache, refetch, deduplicare, retry, paginare, mutații.
const { data, isPending, error } = useQuery({
queryKey: ['posts', { page }], // identitatea datelor în cache
queryFn: () => api(`/api/posts?page=${page}`),
staleTime: 60_000, // 1 min proaspete → fără refetch
})
const queryClient = useQueryClient()
const createPost = useMutation({
mutationFn: (post: NewPost) => api('/api/posts', { method: 'POST', body: JSON.stringify(post) }),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['posts'] }),
})Conceptele cheie
| Concept | Ce înseamnă |
|---|---|
| queryKey | array care identifică datele; tot ce influențează rezultatul intră în cheie (page, filtre) |
| staleTime | cât timp datele sunt „proaspete”; implicit 0 = mereu considerate vechi |
| gcTime | cât rămân în cache după ce nu le mai folosește nimeni (implicit 5 min) |
| invalidateQueries | marchează ca vechi → refetch pentru ce e pe ecran; potrivire pe prefix |
| deduplicare | 3 componente cer ['user', 1] → un singur request |
| refetch automat | la focus pe fereastră, la reconectare, la montare (dacă e stale) |
Optimistic update
Afișezi rezultatul înainte de răspunsul serverului, ca UI-ul să pară instant; dacă eșuează, revii.
| Unde | Cum |
|---|---|
| React 19 / Server Actions | useOptimistic |
| TanStack Query | onMutate (actualizezi cache-ul) + onError (rollback) |
Cum alegi
| Situație | Alegere |
|---|---|
| pagini Next care afișează date (blog, catalog, profil) | Server Components |
| mutații din formulare în Next | Server Actions + updateTag |
| UI foarte interactiv cu date de server: dashboard, infinite scroll, polling, filtre live | TanStack Query (poți face prefetch pe server) |
| SPA fără server propriu | TanStack Query |
| state pur de UI | useState / URL / Zustand |
Pe scurt
- Server state = copie locală a unor date de pe server; se poate învechi oricând.
- Nu-l pune în
useState+useEffect. - Next: Server Components + Server Actions; pe client: TanStack Query cu
queryKey,staleTime, invalidare.