¡Claro que sí! Zustand es una librería de gestión de estados para React (y vainilla JavaScript) conocida por su simplicidad, ligereza y rendimiento. Es una alternativa popular para quienes buscan una solución menos “opinionated” y con menos boilerplate que Redux.
Aquí tienes un “cheatsheet” completo y bien estructurado de Zustand, optimizado para ser claro y conciso para una IA conversacional.
🐻 Zustand Cheatsheet Completo 🐻
Zustand es una librería de gestión de estados pequeña y rápida para React y JavaScript/TypeScript. Se basa en una API sencilla con hooks y evita el boilerplate, las inyecciones de proveedores (Provider) y el contexto complejo, lo que la hace muy fácil de aprender y usar.
1. 🌟 Conceptos Clave
- Store (Tienda): Un lugar centralizado donde resides el estado de tu aplicación. En Zustand, cada store es una función creada con
create(). - Hooks-based: Interactúa con el store dentro de componentes React usando hooks generados automáticamente.
- No Boilerplate: Mínimo código para configurar y usar.
- No
ProviderComponent: No necesitas envolver tu aplicación con un componenteProvider(como en Redux o Context API). Los stores se pueden usar directamente en cualquier componente. - Estado Inmutable: Aunque los métodos
setde Zustand permiten una sintaxis de “mutación” (similar a Immer), internamente garantizan la inmutabilidad creando nuevas copias del estado. - Direct Access: Puedes acceder y manipular el estado fuera de los componentes de React, lo que es útil para lógica de negocio en servicios o utilidades.
2. 🛠️ Configuración Inicial
- Instalación:
npm install zustand # o yarn add zustand # o pnpm add zustand - Importación Básica:
import { create } from 'zustand';
3. 🚀 Creación de un Store
Un store de Zustand es simplemente una función que devuelve un objeto con el estado inicial y las funciones para modificarlo.
// src/stores/bearStore.js
import { create } from 'zustand';
// 'create' recibe una función que tiene 'set' (para modificar estado) y 'get' (para leer estado)
const useBearStore = create((set, get) => ({
// 1. Estado inicial
bears: 0,
fish: 0,
location: 'forest',
// 2. Acciones (funciones para modificar el estado)
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), // Incrementa 'bears'
removeAllBears: () => set({ bears: 0 }), // Establece 'bears' a 0
addFish: (amount) => set((state) => ({ fish: state.fish + amount })),
// Ejemplo de acción que usa 'get' para leer el estado actual
resetAll: () => {
// get() permite acceder al estado actual
const currentBears = get().bears;
console.log(`Reseteando ${currentBears} osos.`);
set({ bears: 0, fish: 0, location: 'forest' });
},
}));
export default useBearStore;
4. 🧰 Acceder y Modificar el Estado en Componentes React
Utiliza el hook generado por tu store (useBearStore en este ejemplo) para acceder al estado y las acciones.
import React from 'react';
import useBearStore from '../stores/bearStore'; // Importa tu store
function BearCounter() {
// 1. Acceder a un valor específico del estado
const bears = useBearStore((state) => state.bears); // Este componente solo se re-renderiza si 'bears' cambia
// 2. Acceder a una acción
const increasePopulation = useBearStore((state) => state.increasePopulation);
const removeAllBears = useBearStore((state) => state.removeAllBears);
const resetAll = useBearStore((state) => state.resetAll);
return (
<div>
<h2>Contador de Osos: {bears}</h2>
<button onClick={increasePopulation}>Añadir Oso</button>
<button onClick={removeAllBears}>Remover Todos los Osos</button>
<button onClick={resetAll}>Resetear Todo</button>
</div>
);
}
function FishCounter() {
// 3. Acceder a múltiples partes del estado o acciones
const fish = useBearStore((state) => state.fish);
const addFish = useBearStore((state) => state.addFish);
return (
<div>
<h3>Contador de Peces: {fish}</h3>
<button onClick={() => addFish(1)}>Añadir Pez</button>
<button onClick={() => addFish(5)}>Añadir 5 Peces</button>
</div>
);
}
// Ejemplo de componente Padre
function App() {
return (
<div>
<h1>Aplicación de Gestión de Osos</h1>
<BearCounter />
<FishCounter />
</div>
);
}
export default App;
5. 🚀 Lógica Asíncrona
Maneja llamadas a APIs o cualquier operación asíncrona directamente dentro de las acciones del store.
// src/stores/userStore.js
import { create } from 'zustand';
const useUserStore = create((set) => ({
user: null,
isLoading: false,
error: null,
fetchUser: async (userId) => {
set({ isLoading: true, error: null }); // Establece estado de carga
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!response.ok) {
throw new Error('Error al obtener usuario');
}
const data = await response.json();
set({ user: data, isLoading: false }); // Actualiza el estado con los datos
} catch (err) {
set({ error: err.message, isLoading: false, user: null }); // Maneja el error
}
},
}));
export default useUserStore;
Uso en Componente:
import React, { useEffect } from 'react';
import useUserStore from '../stores/userStore';
function UserProfile({ userId }) {
const { user, isLoading, error, fetchUser } = useUserStore((state) => ({
user: state.user,
isLoading: state.isLoading,
error: state.error,
fetchUser: state.fetchUser,
}));
useEffect(() => {
fetchUser(userId); // Dispara la acción asíncrona al montar o cambiar userId
}, [userId, fetchUser]);
if (isLoading) return <div>Cargando perfil...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>No se encontró usuario.</div>;
return (
<div>
<h3>Perfil de {user.name}</h3>
<p>Email: {user.email}</p>
<p>Teléfono: {user.phone}</p>
</div>
);
}
export default UserProfile;
6. 🔍 Selectores (Optimización de Re-renders)
Para evitar re-renders innecesarios, selecciona solo las partes del estado que tu componente realmente necesita.
- Selección Simple: El componente solo re-renderiza si
state.bearscambia.const bears = useBearStore((state) => state.bears); - Selección de Múltiples Propiedades: Si seleccionas múltiples propiedades en un objeto (o desestructuras), el componente re-renderizará si cualquiera de esas propiedades cambia.
// Este componente re-renderiza si bears O location cambian const { bears, location } = useBearStore((state) => ({ bears: state.bears, location: state.location, })); - Comparación “Shallow” (
shallow): Para objetos o arrays que se re-crean pero su contenido es el mismo, usashallowpara una comparación superficial.import { shallow } from 'zustand/shallow'; // Este componente re-renderiza solo si 'bears' o 'location' cambian sus valores const { bears, location } = useBearStore( (state) => ({ bears: state.bears, location: state.location, }), shallow // Usa la comparación superficial ); - Comparación Personalizada: Puedes proporcionar tu propia función de comparación como segundo argumento al hook.
7. 💾 Persistencia de Estado (Middleware persist)
Para almacenar el estado en localStorage, sessionStorage o cualquier otro almacenamiento.
// src/stores/persistedStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware'; // Importa el middleware
const usePersistedStore = create(
persist(
(set, get) => ({
count: 0,
lastUpdated: Date.now(),
increment: () => set((state) => ({ count: state.count + 1, lastUpdated: Date.now() })),
reset: () => set({ count: 0, lastUpdated: Date.now() }),
}),
{
name: 'my-app-storage', // Nombre clave en el almacenamiento local
getStorage: () => localStorage, // O sessionStorage, o un almacenamiento personalizado
// opcional: partialize: (state) => Object.fromEntries(Object.entries(state).filter(([key]) => !['nonPersistedKey'].includes(key))), // Guardar solo ciertas partes
// opcional: blacklist: ['nonPersistedKey'], // No guardar estas claves
// opcional: whitelist: ['persistedKey'], // Solo guardar estas claves
// opcional: version: 1, // Para migraciones de esquema
}
)
);
export default usePersistedStore;
Uso: Funciona igual que cualquier otro store de Zustand.
8. 🧰 Middlewares Comunes
Zustand soporta middlewares para extender la funcionalidad del store. Se aplican envolviendo la función create o el persist middleware.
devtools: Para integrar con la extensión Redux DevTools.import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; const useDevToolsStore = create( devtools( (set) => ({ clicks: 0, increment: () => set((state) => ({ clicks: state.clicks + 1 })), }), { name: 'MyDevToolsStore' } // Nombre que aparece en DevTools ) );immer: Simplifica las actualizaciones de estado inmutables usando una sintaxis mutable (como en Redux Toolkit).import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; const useImmerStore = create( immer((set) => ({ user: { name: 'Alice', age: 30, address: { city: 'NY' } }, updateCity: (newCity) => set((state) => { state.user.address.city = newCity; // "Mutación" segura gracias a Immer }), })) );- Orden de los Middlewares: Se anidan de adentro hacia afuera. El middleware más externo se ejecuta primero. Por ejemplo,
create(devtools(persist(...)))significadevtoolsenvuelve apersist.
9. 💡 Buenas Prácticas y Consejos
- Atomic Stores: Crea stores pequeños y enfocados en una única pieza de estado o característica (ej.
useAuthStore,useCartStore,useUserStore). Esto mejora la modularidad. - Inmutabilidad: Aunque
setyimmerlo manejan por ti, piensa siempre en términos de inmutabilidad: las acciones “devuelven” un nuevo estado, no modifican el existente. - Selectores para Optimización: Siempre que uses
useStoreen un componente, pasa una función selectora para extraer solo las propiedades que necesitas. Usashallowsi extraes múltiples propiedades para evitar re-renders innecesarios. - Separar Acciones y Estado (opcional): Para stores grandes, puedes organizar las acciones en un objeto separado y luego esparcirlas en la definición del store.
- TypeScript: Zustand tiene un excelente soporte de TypeScript. Define las interfaces para tu estado y para la función del store para una experiencia de desarrollo tipo-segura.
// type State = { bears: number; }; // type Actions = { increasePopulation: () => void; }; // const useBearStore = create<State & Actions>((set) => ({ ... })); - DevTools: Siempre usa el middleware
devtoolsen desarrollo. Es invaluable para la depuración. - Persistencia Cuidadosa: Decide qué partes del estado realmente necesitan persistir. Usa
whitelistoblacklistsi no quieres guardar todo el estado.
Este cheatsheet te proporciona una referencia completa de Zustand, cubriendo sus conceptos esenciales, cómo crear y usar stores, manejar lógica asíncrona, optimizar re-renders, persistir el estado y aplicar middlewares, junto con las mejores prácticas para construir aplicaciones React eficientes y fáciles de mantener.