Estado de aplicación en React
La pregunta no es «qué librería de estado usar», sino qué representa el dato, quién lo necesita y cuál es su fuente de verdad. Elegir la ubicación correcta reduce sincronizaciones, efectos y cachés manuales.
Clasifica el dato antes de guardarlo
| Tipo | Ejemplos | Lugar inicial | Señal de que debe subir |
|---|---|---|---|
| Estado de interfaz local | modal abierto, input controlado, pestaña activa | useState en el componente | un hermano o ancestro también debe modificarlo |
| Estado compartido del cliente | preferencias de UI, carrito local, usuario ya cargado | ancestro común; Context si atraviesa varias capas | actualizaciones frecuentes o dominios complejos |
| Estado de URL | búsqueda, página, ordenación, filtro | parámetros de ruta o query string | debe sobrevivir a recarga, historial o enlace compartible |
| Estado persistido en navegador | tema, borrador no sensible | localStorage o cookie de preferencia | se necesita entre sesiones, no en el servidor |
| Estado de servidor | lista remota, perfil, permisos, saldo | caché/consulta de datos, por ejemplo Tanstack Query | hay revalidación, errores, carga o invalidación |
No copies un estado de servidor a useState solo para mostrarlo: crea dos fuentes de verdad y obliga a sincronizarlas. Deriva los valores que puedas durante el renderizado y conserva solo el mínimo dato editable.
Secuencia de decisión
- ¿Solo un componente lo lee y modifica? Empieza con React useState.
- ¿Debe aparecer en un enlace, usar el botón Atrás o poder compartirse? Llévalo a la URL.
- ¿Procede de una API o base de datos? Mantén el dato en una capa de consulta y sincronización; no en un store global por defecto.
- ¿Varios componentes del mismo árbol lo necesitan? Eleva el estado al ancestro común. React Context es apropiado para datos relativamente estables que muchos descendientes consumen.
- ¿Debe sobrevivir a cerrar el navegador? Persiste únicamente preferencias o borradores no sensibles con useLocalStorage o una cookie. La sesión y los secretos los controla el servidor.
Ejemplo: filtro compartible
Un término de búsqueda que solo sirve para una vista puede empezar local. Cuando el enlace debe preservar el filtro, la URL es la fuente de verdad:
const params = new URLSearchParams(window.location.search);
const query = params.get("q") ?? "";
function updateQuery(next: string) {
params.set("q", next);
window.history.replaceState(null, "", `?${params}`);
}En un framework, usa su API de router para que la navegación y el renderizado permanezcan coordinados. El ejemplo ilustra la decisión de modelado, no sustituye el manejo de rutas de NextJS o Remix.
Límites y errores habituales
- Context no reemplaza una caché de servidor: una actualización puede rerenderizar a muchos consumidores y no resuelve reintentos, invalidación ni datos obsoletos.
localStoragees accesible desde JavaScript. No guardes tokens, credenciales ni información privada.- Una cookie con
HttpOnlyno se lee desde React por diseño; es la opción adecuada para sesiones que debe gestionar el servidor. - Evita efectos que copian un valor derivable. Por ejemplo, calcula
items.filter(...)en renderizado antes de crear otro estado para la lista filtrada.