Configurar una máquina de estados en React te permite controlar el flujo de un formulario tipo wizard con reglas claras de avance entre pasos. Aquí construyes el objeto stateMachineConfig que define el paso inicial, los validadores y las vistas que se renderizan en cada etapa, ideal si trabajas con formularios multi paso y quieres evitar lógica condicional dispersa.
Cómo defines la configuración inicial de la máquina de estados
La configuración arranca con un objeto que centraliza tres piezas: el paso inicial, los pasos con sus blockers y las vistas asociadas. Antes del componente App, creas la constante stateMachineConfig siguiendo la interfaz que ya tenías lista [00:14].
El initialStep marca por dónde empieza el flujo. En este formulario, lo defines en step1 para que el usuario comience capturando su nombre [00:42].
¿Qué es un blocker en una máquina de estados? Es una condición booleana que decide si un paso puede avanzar al siguiente. Si devuelve true, el flujo continúa; si devuelve false, el usuario se queda en la misma vista.
Cómo configuras los pasos con sus validaciones
Cada paso recibe una key y un blocker que evalúa el estado actual. Aquí defines tres pasos con reglas distintas:
- step1: avanza solo si
Boolean(state.name) es verdadero. La conversión a booleano garantiza que un string vacío bloquee el paso [01:24].
- step2: valida la edad usando el mismo patrón sobre
state.age [01:54].
- confirmation: siempre devuelve
true porque es la pantalla final del wizard [02:09].
La idea es que cada blocker sea una función pura que reciba el estado y retorne un booleano, sin efectos secundarios.
Cómo creas las vistas para cada paso del wizard
Después de los pasos, agregas la propiedad views dentro del mismo objeto. Cada key coincide con el nombre del paso y apunta a una función que renderiza el componente correspondiente [02:34].
La vista de step1 renderiza un div con un input controlado. El value se enlaza a state.name y el onChange actualiza el estado preservando los valores anteriores [03:14]:
jsx
step1: (state, setState) => (
<div>
<input
value={state.name}
onChange={(e) =>
setState((prev) => ({ ...prev, name: e.target.value }))
}
placeholder="Full name"
/>
</div>
)
El spread ...prev evita que al cambiar el nombre se borre la edad ya capturada. Es un patrón clave cuando manejas estados con varias propiedades [04:14].
Cómo replicas la vista para edad y confirmación
La vista de step2 repite la estructura de step1, pero con tipo number y conversión explícita usando parseInt sobre e.target.value. Esto asegura que el estado guarde un entero y no un string numérico [05:30].
La vista confirmation no necesita setState, solo lee el estado para mostrar el resumen final [05:57]:
jsx
confirmation: (state) => (
<p>{state.name} is {state.age} years old</p>
)
¿Por qué usar parseInt en el input de edad? Porque los inputs de HTML siempre devuelven strings, incluso con type="number". Convertir el valor evita errores cuando luego compares la edad con números reales.
Cómo tipas la configuración con TypeScript
Los errores que aparecen en el editor vienen de que prev y los parámetros de las vistas no tienen tipo inferido. La solución es aplicar el tipo StateMachineConfig directamente sobre la constante [06:32].
Ese tipo recibe dos genéricos:
- WizardState: la forma del estado de la aplicación, con
name y age.
- Nombres de los steps: una unión de strings literales como
'step1' | 'step2' | 'confirmation'.
Con esos genéricos, TypeScript infiere automáticamente el tipo de state, setState y prev dentro de cada vista, y bloquea errores si escribes mal el nombre de un paso.
Con esta configuración ya tienes el esqueleto completo: pasos con reglas de avance, vistas conectadas al estado y tipos que protegen el flujo. El siguiente movimiento es crear la lógica del componente general que consume esta máquina y permite navegar entre pasos. ¿Cómo manejarías tú los blockers si necesitaras validaciones asíncronas? Cuéntame en los comentarios.