Guía Completa para Dominar Zustand en React

Zustand es una librería ligera y flexible para gestionar el estado en aplicaciones React. Ofrece una API simple pero poderosa, eliminando la necesidad de acciones, reducers y middlewares complejos.

Es ideal para manejar estados pequeños o grandes de forma rápida, eficiente y escalable, y representa una alternativa sencilla a opciones más complejas como Redux o Context API.


  Ventajas de usar Zustand

  • Menos código repetido: Comparado con Redux, reduce la necesidad de boilerplate.
  • Documentación clara: Fácil de entender, incluso para principiantes.
  • Flexibilidad:
    • Puedes usarlo de forma sencilla o con TypeScript.
    • Permite integrar herramientas como immer para manejar inmutabilidad.
    • Compatible con patrones avanzados, como el uso de reducers y dispatch similares a Redux.
  • Sin necesidad de un proveedor: No requiere envolver toda la aplicación con un componente proveedor, como en Redux.
  • Actualizaciones optimizadas: Solo renderiza los componentes afectados por cambios en el estado, mejorando el rendimiento.
  • Fácil de usar: Se basa en los hooks nativos de React, lo que lo hace intuitivo.
  • API simple y concisa: Facilita la definición y actualización del estado.
  • Rendimiento eficiente: Diseñado para ser rápido y optimizado.
  • Estado global accesible: Cualquier componente de la aplicación puede acceder al estado sin complicaciones.

Conceptos Básicos de Zustand


  1. Store: Es el lugar donde se almacena el estado global.
  2. Selector: Permite obtener partes específicas del estado global, optimizando re-renderizados.
  3. Middleware: Extiende funcionalidades, como persistencia en localStorage o logs.
  4. Sin boilerplate: Configuración mínima en comparación con Redux.

1. Instalación de Zustand 🚀

Instala Zustand usando npm o yarn:

bash
npm install zustand
# o
yarn add zustand

2. Cómo Crear un Store

Para configurar Zustand en nuestro proyecto, es necesario definir un store. Podemos crear tantos stores como necesitemos, ya que cada uno puede estar diseñado para manejar estados específicos dentro de nuestra aplicación.

  Recomendación

Es buena práctica organizar los stores en una carpeta dedicada llamada store. Dentro de esta carpeta, crea un archivo como store.jsx o store.tsx, dependiendo de si usas JavaScript o TypeScript.

Esta estructura facilita la organización y escalabilidad del proyecto.

En Zustand, defines el estado y las acciones en un archivo central. Aquí un ejemplo básico:

jsx
// store.js
import { create } from 'zustand';

const useStore = create((set) => ({
    count: 0, // Estado inicial del contador
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
  })
);

export default useStore;

create: Es una función que Zustand exporta para crear tu store (almacén).


1. Importación de la función create

El propósito principal de la función create es inicializar el estado global y definir las acciones (funciones que cambian ese estado). Es similar a inicializar un useState, pero con alcance global.

jsx
import { create } from 'zustand';

2. Definición del Store

jsx
const useStore = create((set) => ({
  count: 0, // Estado inicial del contador
  increment: () => set((state) => ({ count: state.count + 1 })),  // Incrementar el contador
  decrement: () => set((state) => ({ count: state.count - 1 })), // Decrementar el contador
  })
);

A continuación, describiremos algunos puntos clave que te ayudarán a comprender mejor el funcionamiento de Zustand:

  Explicación del flujo

  • useStore: Es un hook personalizado que Zustand genera para ti. Permite acceder al estado global y las acciones en cualquier componente de React.
  • set: Función de Zustand para actualizar el estado global (similar a setState en React).
  • count: 0: Estado inicial del contador.
  • increment: Es una Función que usa la función set para aumentar el valor de count en 1.
  • decrement: Es una Función que usa la función set para disminuir el valor de count en 1.
  • (state): Función que recibe el estado actual del store (a través de state, en este caso, un objeto que contiene count).
  • state.count: Es un acceso al valor actual de la propiedad count dentro del estado.
  • { count: state.count + 1 } actualiza count aumentando en 1.
  • { count: state.count - 1 } actualiza count disminuyéndolo en 1.
  • { count: state.count + 1 } y { count: state.count - 1 }: Son los objetos que devuelven el nuevo estado actualizado de count.
  • + 1: Suma 1 al valor actual de count.
  • { count: ... }: Devuelve un nuevo objeto con el valor actualizado de count.
  • set(...): es crucial porque le dice a Zustand que debe aplicar el cambio en el estado global. Dentro de set(), pasas una función que toma el estado actual (state) y devuelve el nuevo estado, lo cual es fundamental en Zustand para actualizar el store de manera reactiva.

  Resumen

En Zustand, set es una función que se usa para actualizar el estado. Se pasa como argumento a una función que recibe el estado actual (denominado state). Dentro de esa función, modificamos el estado y devolvemos un nuevo objeto con los valores actualizados. Por ejemplo, set((state) => ({ count: state.count + 1 })) actualiza count incrementándolo en 1. La función set permite que las modificaciones sean aplicadas globalmente.

3. Uso del Store en un Componente de React

Una vez que hemos definido nuestro store, podemos utilizarlo en cualquier página o componente de nuestra aplicación. Para ello, simplemente debemos importarlo y extraer los atributos o métodos necesarios.

Pasos:


  1. Importar el store: Trae el store en el componente donde lo necesitas.
  2. Acceder al estado y las acciones: Usa el hook useStore para interactuar con el estado y las funciones definidas en el store.

Esto permite aprovechar el estado global de manera sencilla y eficiente dentro de tus componentes.

jsx
// App.js

// Importando el store useStore
import useStore from './store/store';

const Counter = () => {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);
  const decrement = useStore((state) => state.decrement);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

export default Counter;
  Reglas Básicas de Zustand

  1. Hooks React: Usa useStore dentro de componentes funcionales o hooks personalizados.
  2. Optimización automática: Los selectores permiten evitar re-renderizados innecesarios.
  3. Actualización inmutable: set debe retornar un nuevo estado, no mutar el existente.

Casos de Uso Prácticos


Estado Global Simple

Úsalo para manejar estados como el tema claro/oscuro o el idioma:

jsx
const useThemeStore = create((set) => ({
theme: 'light',
toggleTheme: () =>
  set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
}));

Manejo de Carritos

Gestión de un carrito de compras:

jsx
const useCartStore = create((set) => ({
cart: [],
addToCart: (item) => set((state) => ({ cart: [...state.cart, item] })),
removeFromCart: (id) =>
  set((state) => ({ cart: state.cart.filter((item) => item.id !== id) })),
}));

Persistencia con Middleware

Persistir datos en localStorage usando un middleware:

jsx
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(
persist(
  (set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
  }),
  { name: 'counter-storage' } // Clave en localStorage
)
);

Autenticación

Manejo de usuario autenticado:

jsx
const useAuthStore = create((set) => ({
user: null,
login: (userData) => set(() => ({ user: userData })),
logout: () => set(() => ({ user: null })),
}));

Ejemplo Completo


Archivo store.js:

jsx
import { create } from 'zustand';

const useStore = create((set) => ({
count: 0,
theme: 'light',
increment: () => set((state) => ({ count: state.count + 1 })),
toggleTheme: () =>
  set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
}));

export default useStore;

Archivo App.js:

jsx
import useStore from './store/store';

const App = () => {
const count = useStore((state) => state.count);
const theme = useStore((state) => state.theme);
const increment = useStore((state) => state.increment);
const toggleTheme = useStore((state) => state.toggleTheme);

return (
  <div style={{ backgroundColor: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
    <h1>Count: {count}</h1>
    <button onClick={increment}>Increment</button>
    <button onClick={toggleTheme}>Toggle Theme</button>
  </div>
);
};

export default App;

Resumen de Funciones Principales

FunciónDescripción
createCrea el “store” con estado y acciones.
setPermite actualizar el estado global.
useStoreHook personalizado que accede al estado o acciones del “store”.
(state) => ...Recibe el estado actual y retorna un nuevo estado, respetando la inmutabilidad.

Recursos Adicionales