React 19 de Cero a Experto / Estado global
Zustand: estado global sin ceremonia
Cuando el estado global cambia seguido o quieres que solo re-rendericen los componentes que usan la
parte que cambió, Context se queda corto. La herramienta favorita del ecosistema React para eso es
ZUSTAND: un store minúsculo, sin Provider, con selectores. Y aquí no solo la vas a usar —la vas a
CONSTRUIR—: tu propio mini-Zustand en ~15 líneas que corre en vivo, porque por dentro es
EXACTAMENTE el store observable que hiciste en JavaScript (SC-02, M13). Luego ves la real.
El marcador del estadio, otra vez
Construye tu mini-Zustand (en vivo)
Un store de Zustand es un objeto con estado y acciones, más una forma de suscribirse. React 18 trae
el hook exacto para conectarlo: useSyncExternalStore. Aquí está tu propio crearStore corriendo:
Agrega y borra gastos: Form, Lista y Total comparten el mismo bolsillo SIN un Provider ni
una sola prop entre ellos. Cada uno se suscribe con un selector (s => s.gastos) y re-renderiza
cuando esa parte cambia. Ese crearStore es el store observable del SC-02 (Set de oyentes,
setState inmutable que avisa, subscribe que devuelve la desuscripción); useSyncExternalStore es
el puente oficial de React para conectarlo. Acabas de construir el 80% de Zustand.
El Zustand real
La librería es ese mismo patrón, con una API pulida. Compara —es casi idéntico a tu store—:
import { create } from "zustand";
type BolsilloState = {
gastos: Gasto[];
agregar: (nombre: string, valor: number) => void;
borrar: (id: number) => void;
};
// create() ES tu crearStore: recibe (set) y devuelve estado + acciones
const useBolsillo = create<BolsilloState>((set) => ({
gastos: [],
agregar: (nombre, valor) =>
set((s) => ({ gastos: [...s.gastos, { id: Date.now(), nombre, valor }] })),
borrar: (id) => set((s) => ({ gastos: s.gastos.filter((g) => g.id !== id) })),
}));
// en cualquier componente, sin Provider, con selector:
function Total() {
const gastos = useBolsillo((s) => s.gastos); // se suscribe SOLO a gastos
const total = gastos.reduce((sum, g) => sum + g.valor, 0);
return <p>Total: {total}</p>;
}create((set) => ({...})) es tu crearStore; el hook que devuelve (useBolsillo) es tu useStore
con el store ya incorporado; el selector (s) => s.gastos es idéntico. Zustand añade cosas útiles
—middleware para persistir en localStorage, integración con Redux DevTools, subscribe fuera de
React— pero el modelo mental es el que acabas de construir. Se instala con pnpm add zustand.
¿Qué ventaja tiene Zustand sobre Context para estado global que cambia seguido?
Mini-reto
Amplía tu mini-Zustand: 1) agrega al store una acción limpiar() que vacíe los gastos, y un botón
que la use; 2) agrega un componente Contador que se suscriba SOLO a s => s.gastos.length y muestre
el número —comprueba que funciona igual que los demás—; 3) en un comentario, explica por qué el store
vive FUERA del componente App (pista: para que sobreviva y se comparta, como el Map de caché del
módulo 5). Siente que es el store del SC-02 con el enchufe de React.
Qué sigue
Context y Zustand guardan "qué datos hay". Pero algunos estados no son datos sueltos sino FLUJOS con reglas: "de "cargando" solo se puede ir a "éxito" o "error", nunca a "inactivo"". Eso son las MÁQUINAS DE ESTADO, y la próxima lección las construye (tu propia mini-máquina en vivo, la lavadora del SC-02 revisitada) antes de mostrar XState.