Al hablar del estado global de una aplicación, nos referimos a los datos que necesitan ser accesibles en múltiples componentes, sin importar su ubicación en el árbol de componentes.
Durante años, la gestión de estado global en React tuvo un nombre Redux. Redux era potente, pero absurdamente complejo. Requiere escribir una cantidad inmensa de código repetitivo para hacer cosas sencillas.
Después llegó la Context API, que es fácil de usar pero tiene problemas de rendimiento si no se tiene cuidado.
Entonces llegó ❤️ Zustand a solucionarlo todo. Una librería que elimina la complejidad, elimina los <Provider> que hace que gestionar el estado global sea tan fácil como declarar una variable.
¿Por qué Zustand?
Zustand (que significa “Estado” en alemán) propone una filosofía minimalista:
- Sin providers: No necesitas envolver tu
<App>con nada - Menos código: Definir un store ocupa unas pocas líneas
- Rendimiento: Los componentes solo se re-renderizan si cambia exactamente el dato que están escuchando
Instalación de Zustand
Para comenzar a usar Zustand, primero debemos instalarlo en nuestro proyecto:
npm install zustand
Una vez instalada, ya podemos empezar a usarla. Así de fácil.
Creación de un store con Zustand
En Zustand, el estado vive en un concepto llamado Store. Un store es simplemente un objeto que contiene:
- Los valores (estado)
- Las funciones para actualizarlos (acciones).
Por ejemplo, vamos a ver como crear un Store para gestionar un carrito de la compra (por no hacer otro contador).
Cread el archivo src/store/useCartStore.js.
import { create } from 'zustand';
// create recibe una función callback que nos da el método 'set'
export const useCartStore = create((set) => ({
// 1. Estado inicial (Variables)
items: 0,
totalPrice: 0,
// 2. Acciones (Funciones para modificar el estado)
addItem: () => set((state) => ({
items: state.items + 1
})),
increasePrice: (amount) => set((state) => ({
totalPrice: state.totalPrice + amount
})),
removeAll: () => set({ items: 0, totalPrice: 0 }),
}));
Fijaos en la función set. A diferencia de useState, aquí no se reemplaza todo el estado, sino que se fusiona.
Si hacemos set({ items: 0 }), la propiedad totalPrice se queda intacta. No hace falta copiarla manualmente con ...state.
Consumiendo el estado en componentes
Para usar el estado, simplemente importamos nuestro hook useCartStore.
import { useCartStore } from '../store/useCartStore';
function BadgeCarrito() {
// Solo nos suscribimos a 'items'.
// Si cambia el precio, este componente NO se re-renderiza
const items = useCartStore((state) => state.items);
return <span className="badge">{items}</span>;
}
Para usar las acciones (que son funciones y nunca cambian), podemos extraerlas igual:
function BotonComprar() {
const addItem = useCartStore((state) => state.addItem);
return <button onClick={addItem}>Añadir al carrito</button>;
}
También podríamos haber hecho esto, pero en este caso, si totalPrice cambia, el componente se re-renderizaría. Por eso es mejor hacerlo de la otra forma.
// ⚠️ Si cambia 'totalPrice', este componente renderiza igual
const { items, addItem } = useCartStore();
Persistencia de datos en LocalStorage
¿Queréis que el carrito de la compra no se pierda si el usuario recarga la página? Zustand trae un middleware llamado persist que guarda automáticamente vuestro estado en localStorage (o sessionStorage) y lo hidrata al arrancar.
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
export const useCartStore = create(
persist(
(set) => ({
items: 0,
addItem: () => set((state) => ({ items: state.items + 1 })),
}),
{
name: 'cart-storage', // Nombre de la key en localStorage
}
)
);
Solo con añadir ese envoltorio, vuestro estado es persistente. Sin escribir ni una línea de localStorage.setItem.
DevTools
Si echáis de menos la extensión de navegador de Redux para ver cómo cambia el estado en el tiempo, ¡buenas noticias! Zustand es compatible con Redux DevTools mediante otro middleware.
import { devtools } from 'zustand/middleware';
const useStore = create(devtools((set) => ({ ... })));
A menos que estéis trabajando en un proyecto legacy que use Redux, o en una aplicación empresarial masiva con requisitos muy específicos de flujo de datos, Zustand debería ser vuestra elección por defecto.
