AI SYNTHESIZED • 150 SHEETS
v1.0.0

¡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 Provider Component: No necesitas envolver tu aplicación con un componente Provider (como en Redux o Context API). Los stores se pueden usar directamente en cualquier componente.
  • Estado Inmutable: Aunque los métodos set de 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

  1. Instalación:
    npm install zustand
    # o
    yarn add zustand
    # o
    pnpm add zustand
  2. 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) =&gt; state.fish);
  const addFish = useBearStore((state) =&gt; state.addFish);

  return (
    <div>
      <h3>Contador de Peces: {fish}</h3>
      <button onClick={() =&gt; addFish(1)}>Añadir Pez</button>
      <button onClick={() =&gt; 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) =&gt; ({
  user: null,
  isLoading: false,
  error: null,

  fetchUser: async (userId) =&gt; {
    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) =&gt; ({
    user: state.user,
    isLoading: state.isLoading,
    error: state.error,
    fetchUser: state.fetchUser,
  }));

  useEffect(() =&gt; {
    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.bears cambia.
    const bears = useBearStore((state) =&gt; 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) =&gt; ({
      bears: state.bears,
      location: state.location,
    }));
  • Comparación “Shallow” (shallow): Para objetos o arrays que se re-crean pero su contenido es el mismo, usa shallow para 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) =&gt; ({
        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) =&gt; ({
      count: 0,
      lastUpdated: Date.now(),
      increment: () =&gt; set((state) =&gt; ({ count: state.count + 1, lastUpdated: Date.now() })),
      reset: () =&gt; set({ count: 0, lastUpdated: Date.now() }),
    }),
    {
      name: 'my-app-storage', // Nombre clave en el almacenamiento local
      getStorage: () =&gt; localStorage, // O sessionStorage, o un almacenamiento personalizado
      // opcional: partialize: (state) =&gt; Object.fromEntries(Object.entries(state).filter(([key]) =&gt; !['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) =&gt; ({
          clicks: 0,
          increment: () =&gt; set((state) =&gt; ({ 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) =&gt; ({
        user: { name: 'Alice', age: 30, address: { city: 'NY' } },
        updateCity: (newCity) =&gt;
          set((state) =&gt; {
            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(...))) significa devtools envuelve a persist.

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 set y immer lo 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 useStore en un componente, pasa una función selectora para extraer solo las propiedades que necesitas. Usa shallow si 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: () =&gt; void; };
    // const useBearStore = create<State & Actions>((set) =&gt; ({ ... }));
  • DevTools: Siempre usa el middleware devtools en desarrollo. Es invaluable para la depuración.
  • Persistencia Cuidadosa: Decide qué partes del estado realmente necesitan persistir. Usa whitelist o blacklist si 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.

Descarga completada