Global State en React, sin chorradas


Tomar la decisión sobre cómo gestionar el Estado de una aplicación en React, puede llegar a ser una tarea bastante abrumadora, y dependiendo del tipo de aplicación hay un montón de opciones.

Un useState de andar por casa, y mover props de componente en componente puede ser una opción, useContext como acercamiento al estado global cuando la cosa empieza a coger cierta dimensión y no te preocupan demasiado el rendimiento, o Redux / Redux Toolkit cuando te sobra el tiempo y quieres impresionar a alguien.

Todas estas opciones son perfectamente válidas según el tipo de aplicación, pero cambiar de una a otra puede ser una taréa muy pesada, y como el 80% de los proyectos que empiezo, no se como van a terminar… Hace ya tiempo que me puse a buscar una opción que permitiese hacer crecer la aplicación sin arrepentirme demasiado de decisiones tomadas.

Necesitaba un gestor de estado ligero, fácil de usar e implementar, y válido tanto para aplicaciones grandes como pequeñas.

La revelación llegó cuando un día para probar Three.js decidí seguir un tutorial para hacer un Clon de Minecraft

minecraft

Tengo un hijo de 8 años y ya se había cansado de su Pokedex 😮‍💨

Ahí es donde descubrí Zustand.

Zustand combina lo mejor de los mundos: es fácil de usar y a la vez tremendamente potente.

  • Proporciona una API minimalista para administrar el estado de tu aplicación, lo que consigue que tu código sea más fácil de mantener y de entender.

  • Con Zustand podrás crear y gestionar el Estado global, sin tener que escribir un montón de código repetitivo.

  • Podrás consumir y actualizar el estado sin tener que depender de Context ni Providers que envuelvan toda la aplicación.

  • Se acavó lo de tener el estado por un lado, las acciones por otro y reducers complejos e infinitos.

  • Para formas más complejas de Estado, puedes Implementar Immer sin problemas.

  • Puedes y debes dividir el estado en Slices para cada funcionalidad, siguiendo el patrón separation of concerns, o como a mi me gusta llamarlo CCPSC.

En definitiva podrás contar con un código bonito, sencillo y limpio y escalable.

Para ilustrarlo, volvamos al ejemplo del Clon de Minecraft.

Piensa que cada cubo de los que puedes crear y eliminar en la aplicación tienen su textura y ocupan su espacio en el plano tridimensional, pueden ser miles de cubos y esto hay que reflejarlo en el Estado.

Con Zustand, consumismos y actualizamos el Estado, de una manera super sencilla y optima.

Aquí tienes todo el estado global de la aplicación:

import { nanoid } from 'nanoid'
import create from 'zustand'

export const useStore = create((set) => ({
  texture: 'dirt',
  cubes: [
    {
      id: nanoid(),
      texture: 'grass',
      pos: [1, 3, 1],
    },
  ],
  addCube: (x, y, z) => {
    set((state) => ({
      cubes: [
        ...state.cubes,
        {
          id: nanoid(),
          texture: state.texture,
          pos: [x, y, z],
        },
      ],
    }))
  },
  removeCube: (id) => {
    set((state) => ({
      cubes: state.cubes.filter((cube) => {
        return cube.id !== id
      }),
    }))
  },
  setTexture: (texture) => {
    set(() => ({ texture }))
  },
}))

Para consumir el estado solo tienes que importar el hook useStore que acabamos de crear y seleccionar que acción o elemento del estado te quieres traer.

import { useStore } from '../hooks/useStore'

export function Cube() {
  const [removeCube, addCube] = useStore((state) => [
    state.removeCube,
    state.addCube,
  ])
	const [cubes] = useStore((state) => [state.cubes])

...
}

Y ya está… Realmente no hay que complicarse la vida.

Como siempre te voy a recomendar que le eches un vistazo a la documentación, aunque en este caso os incluyo también un artículo más detallado sobre el uso de Zustand, y un video del bueno de Midu con una comparativa más detallada.