¡Ven a la JSConf España 2026! Comprar entradas

Store de favoritos con Zustand

En esta clase damos un paso más en la gestión de estado global y creamos una store de favoritos con Zustand. El objetivo es manejar un estado más complejo, con múltiples acciones y lógica reutilizable, sin usar Context, Provider ni dependencias adicionales.

Verás cómo Zustand nos permite definir estado, acciones, estados derivados y helpers de forma clara, optimizada y fácil de testear.

¿Qué vamos a construir?

Una store de favoritos que nos permita:

  • Guardar una lista de IDs favoritos.
  • Añadir favoritos sin duplicados.
  • Eliminar favoritos.
  • Saber si un elemento está marcado como favorito.
  • Alternar favoritos con una sola acción.
  • Calcular estados derivados como el número total de favoritos.

Todo encapsulado en una única store.

Creando la store

Creamos un nuevo archivo para la store:

// favoritesStore.js
import { create } from 'zustand'

export const useFavoritesStore = create((set, get) => ({
  favorites: [],

  addFavorite: (jobId) =>
    set((state) => ({
      favorites: state.favorites.includes(jobId) ? state.favorites : [...state.favorites, jobId],
    })),

  removeFavorite: (jobId) =>
    set((state) => ({
      favorites: state.favorites.filter((id) => id !== jobId),
    })),

  isFavorite: (jobId) => {
    const { favorites } = get()
    return favorites.includes(jobId)
  },

  toggleFavorite: (jobId) => {
    const { isFavorite, addFavorite, removeFavorite } = get()
    isFavorite(jobId) ? removeFavorite(jobId) : addFavorite(jobId)
  },

  favoritesCount: () => get().favorites.length,
}))

Uso de set y get

Zustand nos proporciona dos herramientas clave:

  • set: actualiza el estado.
  • get: permite leer el estado actual sin provocar renders.

Esto nos permite separar claramente:

  • Lógica de actualización.
  • Lógica de consulta.
  • Reutilización de acciones sin duplicar código.

Evitando duplicados

Al añadir un favorito, comprobamos antes si ya existe:

state.favorites.includes(jobId)

Esto evita estados inconsistentes y comportamientos inesperados en la UI.

Acciones reutilizables

Una de las grandes ventajas es poder reutilizar acciones dentro de la propia store:

toggleFavorite: (jobId) => {
  const { isFavorite, addFavorite, removeFavorite } = get()
  isFavorite(jobId) ? removeFavorite(jobId) : addFavorite(jobId)
}

Así evitamos repetir lógica y centralizamos el comportamiento en un solo lugar.

Estados derivados

También podemos definir estados derivados sin almacenarlos explícitamente:

favoritesCount: () => get().favorites.length

Esto nos permite calcular información basada en el estado sin duplicar datos ni preocuparnos por sincronización.

Uso en la UI

En cualquier componente React:

const { favorites, toggleFavorite, isFavorite } = useFavoritesStore()

Sin providers, sin wrappers, sin boilerplate adicional.

Ventajas frente a Context

Comparado con una solución basada en Context:

  • Menos líneas de código.
  • No necesitas Provider.
  • Mejor separación de lógica.
  • Estado más fácil de testear.
  • Actualizaciones más granulares.

Conclusión

Zustand permite crear stores potentes y expresivas con muy poco código. En esta clase has visto cómo manejar un estado realista, con lógica compleja y sin las limitaciones del Context tradicional.