Guía completa para manejar el estado en React con Zustand y TypeScript

Adrian Fernandez
4 min readOct 16, 2024

--

En el ecosistema de React, existen muchas formas de manejar el estado global. Herramientas como Redux y Context API son muy populares, pero a menudo implican una configuración excesiva. Zustand es una alternativa ligera, rápida y fácil de usar que destaca por su simplicidad. En esta guía, aprenderás a integrar Zustand con TypeScript para aprovechar al máximo ambas tecnologías.

¿Qué es Zustand?

Zustand es una librería pequeña pero poderosa para gestionar el estado global en aplicaciones React. Creada por los desarrolladores de Poimandres, quienes también han desarrollado herramientas populares como react-three-fiber, Zustand proporciona una API intuitiva para trabajar con el estado sin la sobrecarga de librerías más complejas como Redux.

¿Por qué Zustand?

Zustand ofrece varias ventajas que la hacen sobresalir en comparación con otras librerías de gestión de estado:

  • Ligera y rápida: No afecta el rendimiento de tu aplicación.
  • Simple de usar: Sin necesidad de configurar reducers, acciones o middlewares complejos.
  • Compatible con TypeScript: Estado seguro y tipado gracias a su integración nativa con TypeScript.
  • Escalable: Perfecta para proyectos pequeños y grandes.

En este tutorial, exploraremos cómo usar Zustand con TypeScript en una aplicación de React.

Instalación

Lo primero es instalar la librería:

npm install zustand

O con yarn:

yarn add zustand

Si trabajas con TypeScript, no necesitas instalar tipos adicionales, ya que Zustand viene con soporte para TypeScript.

Crear un almacén en Zustand con TypeScript

El estado en Zustand se organiza en un “almacén” (store), que contiene tanto el estado como las acciones que lo modifican. Vamos a crear un almacén simple que mantenga el estado de un contador y acciones para incrementarlo y reducirlo.

Definiendo el tipo del estado

Primero, definimos los tipos de nuestro estado y las acciones:

// Definición del tipo del estado
interface CounterState {
count: number;
increase: () => void;
decrease: () => void;
}

Este tipo incluye una propiedad count que será un número, y dos acciones (increase y decrease) que serán funciones para actualizar el estado.

Crear el almacén con Zustand

Ahora, vamos a crear el almacén usando Zustand:

import create from 'zustand';

const useStore = create<CounterState>((set) => ({
count: 0, // Estado inicial
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
}));

export default useStore;

Aquí estamos usando el generador create de Zustand y pasando el tipo CounterState para asegurar que todo el estado y las acciones estén correctamente tipadas.

Usar el almacén en un componente

Ahora que tenemos el almacén, podemos utilizarla en nuestros componentes de React.

Counter.tsx

import React from 'react';
import useStore from './useStore';

const Counter: React.FC = () => {
const { count, increase, decrease } = useStore();

return (
<div>
<h1>Contador: {count}</h1>
<button onClick={increase}>Incrementar</button>
<button onClick={decrease}>Disminuir</button>
</div>
);
};

export default Counter;

En este componente:

  • count es el estado que estamos leyendo desde la tienda.
  • increase y decrease son las acciones para modificar el estado.

Estados derivados y computados

Zustand permite crear estados derivados que dependen de otros valores del estado. Imagina que queremos mostrar un mensaje cuando el contador sea negativo:

Modificar useStore.ts

interface CounterState {
count: number;
isNegative: boolean;
increase: () => void;
decrease: () => void;
}

const useStore = create<CounterState>((set) => ({
count: 0,
isNegative: false, // Estado derivado
increase: () =>
set((state) => ({
count: state.count + 1,
isNegative: state.count + 1 < 0,
})),
decrease: () =>
set((state) => ({
count: state.count - 1,
isNegative: state.count - 1 < 0,
})),
}));

En este caso, isNegative es un estado derivado que cambia automáticamente cuando count es menor que 0.

Modificar Counter.tsx

const Counter: React.FC = () => {
const { count, increase, decrease, isNegative } = useStore();

return (
<div>
<h1>Contador: {count}</h1>
{isNegative && <p>El contador es negativo</p>}
<button onClick={increase}>Incrementar</button>
<button onClick={decrease}>Disminuir</button>
</div>
);
};

Manejo de estados complejos

Zustand también puede manejar objetos y arrays como parte del estado. Vamos a crear una lista de tareas (to-do list) como ejemplo.

useStore.ts (lista de tareas):

interface TaskState {
tasks: string[];
addTask: (task: string) => void;
removeTask: (task: string) => void;
}

const useStore = create<TaskState>((set) => ({
tasks: [],
addTask: (task) => set((state) => ({ tasks: [...state.tasks, task] })),
removeTask: (task) =>
set((state) => ({
tasks: state.tasks.filter((t) => t !== task),
})),
}));

TaskList.tsx

import React, { useState } from 'react';
import useStore from './useStore';

const TaskList: React.FC = () => {
const { tasks, addTask, removeTask } = useStore();
const [newTask, setNewTask] = useState('');

const handleAddTask = () => {
addTask(newTask);
setNewTask('');
};

return (
<div>
<input
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
placeholder="Nueva tarea"
/>
<button onClick={handleAddTask}>Agregar tarea</button>

<ul>
{tasks.map((task, index) => (
<li key={index}>
{task} <button onClick={() => removeTask(task)}>Eliminar</button>
</li>
))}
</ul>
</div>
);
};

export default TaskList;

Persistencia de estado con middleware

Zustand incluye soporte para middleware, como la persistencia del estado en localStorage. Vamos a ver cómo persistir el valor de un contador entre recargas de página.

useStore.ts con persistencia:

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

interface CounterState {
count: number;
increase: () => void;
decrease: () => void;
}

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

export default useStore;

Con esta configuración, el estado del contador se guardará automáticamente en el localStorage, y cuando recargues la página, se restaurará su valor anterior.

Conclusión

Zustand es una excelente opción para manejar el estado en aplicaciones React, y su integración con TypeScript la convierte en una opción aún más atractiva. Es simple, ligera, y perfecta tanto para aplicaciones pequeñas como para proyectos más grandes.

Si estás buscando una alternativa fácil a Redux o quieres algo más sencillo que Context API, ¡prueba Zustand en tu próximo proyecto!

--

--

Adrian Fernandez
Adrian Fernandez

Written by Adrian Fernandez

I'm a Full Stack Web Developer with 10 years of experience. I have dedicated my live to web and mobile experiences.

No responses yet