El estado del servidor va en una capa de caché, no en el estado global
¿Guardas datos del backend en el estado global? Eso es una fábrica de bugs. Cada lista que copias del servidor a un store es una segunda fuente de verdad que empieza a pudrirse en cuanto llega — y cada componente generado por IA que la lee hereda esa podredumbre.
El estado del servidor no es estado del cliente
El estado del cliente es data que tu app posee: la pestaña abierta, el borrador de un formulario, el modo oscuro. Solo cambia cuando tu código lo cambia, así que un store lo modela perfecto.
El estado del servidor es data prestada. Como dice Dominik Dorfmeister (mantenedor de TanStack Query): "tu app no la posee… Es el servidor quien es dueño de los datos." Vive en remoto, llega de forma asíncrona, otras personas pueden cambiarla sin que lo sepas, y puede quedar obsoleta en tu app en cualquier momento. La propia documentación de Redux traza la línea: "data fetching and caching" es un conjunto de problemas distinto de "state management".
Tratar ambos igual es el bug de raíz. Una copia de /api/todos en el store es correcta exactamente hasta que alguien más toca el backend.
Qué te da una capa de caché
Hacer fetch a mano hacia un store implica reimplementar, mal, todo lo que una capa de caché de datos — TanStack (React) Query, SWR, RTK Query, o equivalentes en otros ecosistemas — trae por defecto:
| Problema | Store hecho a mano | Capa de caché |
|---|---|---|
| Caching | Copia manual, sin expiración | Caché por clave; entradas inactivas se recolectan (gcTime por defecto: 5 min en TanStack Query) |
| Deduplicación | Dos componentes = dos requests | Misma clave = un request, resultado compartido |
| Datos obsoletos | Desconocido hasta el reporte de bug | staleTime marca fresh/stale; lo obsoleto se refresca en segundo plano |
| Invalidación | Buscar cada reducer con una copia | Una llamada: invalidateQueries({ queryKey: ['todos'] }) |
| Estados de carga/error | Flags repetidos por endpoint | Devueltos por query, automáticamente |
Hasta el tutorial oficial de Redux admite el boilerplate: thunk asíncrono, request, flags de carga, handlers pending/fulfilled/rejected — por endpoint — y recomienda RTK Query "como el enfoque por defecto para data fetching en apps Redux".
El refactor en un snippet
// Before: server data smeared into a global store
useEffect(() => {
fetch('/api/todos')
.then(r => r.json())
.then(data => dispatch(setTodos(data))); // now stale forever
}, []);
// After: the cache layer owns it
const { data, isPending, error } = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/api/todos').then(r => r.json()),
});
// After any mutation, invalidate — every reader refetches:
queryClient.invalidateQueries({ queryKey: ['todos'] });
Diez componentes pueden llamar ese mismo useQuery y el request se deduplica a uno. Borra el slice, el thunk y los flags de carga.
Por qué importa más con código generado por IA
Los agentes de código imitan los patrones de tu codebase. Si la data del servidor vive en un store, cada componente generado inventa su propio baile de fetch-then-dispatch, cada uno con sus propios bugs de datos obsoletos — los componentes se desincronizan del backend y entre sí. Si la convención es "la data del servidor viene de la capa de caché, indexada por recurso", el código generado converge a un solo patrón correcto: entra una query key, sale data fresca. La arquitectura se vuelve el guardarraíl.
Trucos avanzados
- Ajusta
staleTime, dejagcTimeen paz.staleTimepor defecto es 0 (obsoleto al instante). Súbelo para data que casi no cambia;gcTime(5 min) rara vez necesita cambios. - Invalida por prefijo.
invalidateQueries({ queryKey: ['todos'] })alcanza['todos'],['todos', {type: 'done'}], todo. Agregaexact: truepara acotar, o pasa un predicado para lógica propia. - Reserva el store para lo que queda. Tras la migración, el estado global suele reducirse a data realmente del cliente — muchas veces tan poca que basta un contexto o un store mínimo.
- Revalida al enfocar. Las librerías estilo SWR refrescan al volver a la pestaña o reconectar la red — comportamiento "siempre fresco" gratis que nunca harías a mano.
Recursos
- TanStack Query — Overview (motivación del server state)
- TanStack Query — Guía de query invalidation
- Practical React Query — TkDodo sobre server vs client state
- SWR — Getting started (dedup, caching, revalidación)
- Redux Essentials Parte 7 — RTK Query basics
¿Viste el reel? Este artículo es la versión a fondo del tip #111 de Yeda AI Tips. Yeda AI diseña, audita y entrega flujos de ingeniería asistidos por IA en producción.