En el mundo del desarrollo de aplicaciones con React, la gestión del estado es un componente esencial, pero a menudo complejo. Aquí es donde entra en juego Zustand, una librería elegante y minimalista para el manejo del estado en React.

Zustand se presenta como una solución ligera y eficaz para el manejo del estado en aplicaciones React, ideal para aquellos que buscan simplicidad sin sacrificar la funcionalidad.

¿Qué es Zustand?

Zustand fue creada con la filosofía de mantener las cosas simples y eficientes. A diferencia de otras soluciones de manejo de estado más robustas (y a veces más complejas) como Redux, Zustand ofrece una API sencilla y directa, facilitando la creación y gestión de estados globales, sin la necesidad de envolver componentes en proveedores de contexto o lidiar con una configuración extensa.

Una de las grandes ventajas de Zustand es su enfoque "hook-centric", ya que utiliza hooks de React, como useStore, para acceder y manipular el estado global. Esto se traduce en un código más limpio y declarativo, fácil de leer y mantener.

Además, Zustand no impone una arquitectura específica, lo que permite a los desarrolladores la flexibilidad de estructurar su estado según las necesidades de tu aplicación.

Principales características

  • Sencillez y minimalismo: Zustand tiene un enfoque minimalista, ya que tiene una API muy sencilla y fácil de entender. Esto facilita el aprendizaje y la implementación, incluso para aquellos que son nuevos en el manejo del estado en React.
  • Basado en Hooks: utiliza hooks de React, como useStore, para acceder y manipular el estado. Esto va en la línea de los patrones modernos de desarrollo en React y permite una integración fluida con componentes funcionales.
  • Configuración ligera: a diferencia de otras soluciones de manejo de estado que requieren una configuración más elaborada, Zustand permite configurar el estado global de una manera mucho más directa y sin la necesidad de envolver toda la aplicación en proveedores de contexto.
  • Estado mutable inmutable: aunque suene contradictorio, Zustand combina lo mejor de ambos mundos. Usa inmutabilidad bajo el capó para prevenir efectos secundarios no deseados, pero expone una API mutable, que es más intuitiva, para realizar actualizaciones de estado.
  • No impone arquitectura: Zustand no obliga a los desarrolladores a seguir una arquitectura específica, permitiendo mayor flexibilidad a la hora de estructurar y manejar el estado en aplicaciones de diferentes escalas y complejidades.
  • Rendimiento óptimo: al utilizar hooks y enfocarse en la simplicidad, Zustand ayuda a mantener un alto rendimiento en aplicaciones React, evitando renderizados innecesarios y sobrecarga de memoria.
  • Fácil integración con Middleware: permite la integración con middleware y otras herramientas de desarrollo, como Redux DevTools, lo que facilita la depuración y el seguimiento del estado de la aplicación.
  • Compatibilidad universal: Zustand no está limitado solo a React. Puede usarse con otras bibliotecas o frameworks, lo que lo hace versátil y adaptable a diferentes contextos de desarrollo.

Estas características hacen de Zustand una opción atractiva para desarrolladores que buscan una solución eficiente y fácil de usar para el manejo del estado en sus aplicaciones React.

Instalación

Para testear el funcionamiento de Zustand, vamos a crear un proyecto con React, Vite y TypeScript, aunque la configuración será similar si seleccionamos otro framework o librería como Angular o React.

npm create vite@latest react-zustand-example --  --template react-ts

Para instalar la dependencia de Zustand, ejecutaremos el siguiente comando:

npm install zustand

Configuración

Para configurar Zustand en nuestro proyecto necesitaremos definir nuestro store. Podemos definir tantos stores como queramos, ya que cada uno de ellos puede tener sentido en ciertas partes de nuestra aplicación.

Para definir un store es recomendable utilizar un nuevo fichero. En el siguiente ejemplo vamos a simular el típico contador de incrementar y decrementar que Vite siempre muestra al instalar una nueva aplicación.

Crearemos el fichero counter.store.ts, donde tenemos que diferenciar los siguientes bloques:

  • Definición: será el bloque donde definiremos los datos y acciones que va a tener nuestro store. En el caso de nuestro contador tendremos el atributo count y los métodos de incrementar y decrementar. Podemos definir este bloque como una interface, definiendo en cada caso el tipo de dato.
  • Creación: este bloque será el encargado de definir el valor inicial de cada uno de los atributos de nuestra interfaz, y la manera en la que los métodos se comportan dentro del store.
// counter.store.ts
import create from 'zustand';

// Definición 
interface UserState {
    count: number;
    increment: () => void;
    decrement: () => void;
}

// Creación
const useCounterStore = create<UserState>((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
}));

export default useCounterStore;

Como podemos observar, el valor inicial de nuestro contador es cero y los métodos de incrementar y decrementar suman o restan una unidad.

Uso

Una vez definido nuestro store, podemos utilizarlo en cualquiera de nuestras páginas o componentes dentro de nuestra aplicación. Importándolo y extrayendo los atributos o métodos que vamos a utilizar.

// App.tsx

import './App.css'
import useCounterStore from './store/counter.store.ts';

function App() {
  const { count, increment, decrement } = useCounterStore();
  return (
    <>
      <h1>Vite + React + Zustand</h1>
<div>
            Zustand count: {count}
            <button onClick={increment}>Increment store count</button>
            <button onClick={decrement}>Decrement store count</button>
       </div>
    </>
  )
}

export default App

El ejemplo anterior tendría el mismo funcionamiento que un useState definido a nivel de página o componente.

// App.tsx

import './App.css'
import {useState} from "react";

function App() {
    const [count, setCount] = useState(0)
    return (
        <>
            <h1>Vite + React</h1>
            <div>
                useState count: {count}
                <button onClick={() => setCount((count) => count + 1)}>
                    Increment useState count
                </button>
                <button onClick={() => setCount((count) => count - 1)}>
                    Decrement useState count
                </button>
            </div>
        </>
    )
}

export default App

La principal diferencia entre Zustand y useState de React radica en su alcance y enfoque para manejar el estado en aplicaciones.

Zustand proporciona un sistema de gestión de estado centralizado que permite compartir el estado entre múltiples componentes de manera eficiente.

Por otro lado, useState es una característica de React que se utiliza para gestionar el estado local de componentes individuales de manera más simple y directa. Es útil para componentes más simples o cuando el estado no necesita ser compartido entre varios componentes.

¿Cuándo deberíamos utilizarlo?

Llegados a este punto seguro que ya te estás planteando añadir Zustand a tu proyecto, pero antes de nada deberías saber cuándo deberías utilizarlo frente a useState.

Deberías utilizar Zustand:

  • Si necesitas un estado centralizado. Si tu aplicación tiene múltiples componentes que necesitan acceder al mismo estado, Zustand proporciona un store centralizado que puede ser compartido entre ellos.
  • Si tu aplicación es grande o compleja. Para aplicaciones más grandes o complejas, Zustand puede ofrecer una estructura más organizada y un rendimiento optimizado al gestionar el estado de manera centralizada.
  • Si necesitas actualizaciones inmutables del estado. Zustand utiliza actualizaciones de estado inmutables, lo que puede ayudar a prevenir errores relacionados con el estado mutable en aplicaciones grandes y complejas.
  • Si deseas una solución ligera y simple. Aunque Zustand proporciona un sistema de gestión de estado centralizado, es una biblioteca ligera y fácil de usar, lo que lo hace adecuado incluso para aplicaciones más simples.

Deberías utilizar useState:

  • Si tu estado es local y específico del componente. Si el estado que necesitas manejar solo se utiliza en un componente específico y no necesita ser compartido con otros componentes, useState es una opción adecuada.
  • Si estás construyendo componentes simples. Para componentes más simples y pequeños, useState proporciona una sintaxis más directa y fácil de usar sin la sobrecarga de configurar un store centralizado.
  • Si prefieres una solución nativa de React. useState es parte de React y no requiere la instalación de bibliotecas adicionales, por lo que puede ser la opción preferida si quieres mantener tu aplicación más ligera y utilizar solo las características proporcionadas por React.

En resumen, Zustand es más adecuado para aplicaciones grandes y complejas donde se requiere un estado centralizado y un rendimiento optimizado, mientras que useState es más adecuado para componentes simples y pequeños o cuando prefieres una solución nativa de React.

Persistencia de datos

Cuando actualizas la página en tu aplicación web, se produce una recarga completa de nuestra aplicación. Esto significa que todo el estado de la aplicación, incluido el estado manejado por useState, se restablece a su valor inicial.

En Zustand, al igual que useState, el estado también se pierde cuando se actualiza la página. Esto ocurre porque Zustand almacena su estado en la memoria del navegador y, al igual que con useState, este estado se restablece cuando la página se actualiza o se carga nuevamente.

Sin embargo, con Zustand, puedes usar la extensión zustand-persist para solucionar este “problema”, agregar capacidades de persistencia de estado y evitar que el estado se pierda al actualizar la página.

Al persistir el estado con zustand-persist, el estado se guarda en el almacenamiento local del navegador (como localStorage o sessionStorage), lo que permite conservarlo entre las sesiones de navegación.

import { createJSONStorage, persist } from "zustand/middleware";
import create from "zustand";

type UserState =  {
    count: number;
    increment: () => void;
    decrement: () => void;
}

// Envolver el store con persist y definir el estado inicial
export const useUserStore = create(persist<UserState>(
    (set) => ({
        count: 0,
        increment: () => set((userState) => ({ count: userState.count + 1 })),
        decrement: () => set((userState) => ({ count: userState.count - 1 })),
    }),
    // Opciones de persistencia
    {
        name: 'my-app-state',
        storage: createJSONStorage(() => localStorage), // (opcional) valor por defecto 'localStorage'
    },
));

export default useUserStore;

Como podemos ver en el ejemplo anterior, tendremos que realizar varios cambios con relación a la configuración inicial de nuestro store.

Conclusión

El manejo de estados en React se ha simplificado y potenciado gracias a la biblioteca Zustand. A lo largo de este artículo, hemos explorado en detalle qué es Zustand y sus principales características, desde su instalación y configuración hasta su uso en la construcción de aplicaciones React.

Hemos analizado cuándo sería más apropiado emplear Zustand, destacando sus ventajas en escenarios donde se prefiera una solución ligera y fácil de integrar.

Además, hemos abordado la persistencia de datos, mostrando cómo Zustand puede ser combinado con herramientas como zustand-persist para mantener el estado de la aplicación incluso después de que esta se cierre y vuelva a abrir.

En resumen, Zustand se presenta como una opción poderosa y versátil para el manejo de estados en React, ofreciendo una alternativa eficiente y flexible que vale la pena considerar en tus proyectos de desarrollo web.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.