CodeForge

React 19 de Cero a Experto / Estado global

Máquinas de estado y XState

Teoría24 min20 XP

Algunos estados no son datos sueltos, sino FLUJOS con reglas: de "cargando" solo se puede pasar a "éxito" o "error", nunca volver a "inactivo" de la nada. Modelar eso con booleanos sueltos (cargando, error, listo) lleva a combinaciones imposibles —¿qué significa cargando: true y error: true a la vez?—. Las MÁQUINAS DE ESTADO lo resuelven: estados nombrados y transiciones explícitas. Hoy construyes tu propia mini-máquina en vivo (la lavadora del SC-02, revisitada) y luego ves XState, la librería que las lleva al máximo.

La lavadora que no se salta pasos

Tu mini-máquina, en vivo

Una máquina es una tabla que dice, para cada estado, qué eventos lo llevan a qué otro estado. useReducer (módulo 2) la ejecuta de maravilla. Aquí una máquina de carga de datos corriendo:

Pulsa "Cargar": pasa a cargando, y ~1.2s después a exito o error. "Reintentar" solo funciona desde error. La tabla transiciones es la máquina: cada estado declara a qué otros puede ir, y maquina ignora los eventos inválidos (?? estado). Es IMPOSIBLE estar "cargando y con error" a la vez, porque el estado es UN valor nombrado, no una bolsa de booleanos. Eso elimina toda una categoría de bugs. Y fíjate: es useReducer (módulo 2) con una tabla —máquinas de estado y reducers son parientes cercanos—.

XState: máquinas con esteroides

XState es la librería que lleva esta idea al extremo: define la máquina como un objeto declarativo, con estados, transiciones, acciones, guardas y hasta estados anidados. Compara la estructura con tu tabla —es la misma idea, más formal—:

xstate.tsx
import { createMachine } from "xstate";
import { useMachine } from "@xstate/react";

const maquinaDatos = createMachine({
id: "datos",
initial: "inactivo",
states: {
  inactivo: { on: { CARGAR: "cargando" } },
  cargando: { on: { OK: "exito", FALLO: "error" } },
  exito:    { on: { CARGAR: "cargando" } },
  error:    { on: { REINTENTAR: "cargando" } },
},
});

function Componente() {
const [estado, enviar] = useMachine(maquinaDatos);
return (
  <div>
    <p>Estado: {estado.value}</p>
    <button onClick={() => enviar({ type: "CARGAR" })}>Cargar</button>
  </div>
);
}

createMachine con su states y on ES tu tabla de transiciones, en formato declarativo. useMachine es tu useReducer. Encima, XState añade: ACCIONES (efectos secundarios en las transiciones), GUARDAS (transiciones condicionales), estados ANIDADOS y PARALELOS, actores, y una herramienta visual que DIBUJA tu máquina como un diagrama. Se instala con pnpm add xstate @xstate/react.

¿Qué problema resuelven las máquinas de estado y cuándo conviene XState frente a useReducer?

Mini-reto

Diseña una máquina para un semáforo: 1) estados verde, amarillo, rojo; 2) un solo evento SIGUIENTE que cicle verde → amarillo → rojo → verde; 3) impleméntalo con una tabla de transiciones y useReducer, con un botón que envíe SIGUIENTE y muestre el color. Bonus: agrega un estado intermitente al que solo se llegue con un evento EMERGENCIA desde cualquier estado. Comprueba que es imposible saltarse el orden.

Qué sigue

Ya tienes todas las herramientas de estado: local (useState/useReducer), compartido (Context), global (Zustand), de servidor (TanStack Query), y de flujos (máquinas de estado). La próxima lección las ORDENA en un mapa de decisión —cuál usar para qué, incluido cuándo Redux Toolkit— para que nunca dudes qué sacar. Luego, el reto del módulo.