Validar transiciones con guards en XState

Resumen

Validar transiciones con condiciones es uno de los recursos más potentes de XState, y aquí lo aplicamos para evitar que un usuario avance al ticket sin haber agregado pasajeros. Si estás aprendiendo a construir máquinas de estado en React, este flujo te muestra cómo usar guards y renderizar el contexto en la vista final.

¿Qué es un guard en XState y cómo se implementa?

Un guard es una condición que se evalúa antes de ejecutar una transición. Si retorna true, la máquina avanza al siguiente estado; si retorna false, la transición se ignora y el estado actual se mantiene.

En el proyecto partimos del evento done dentro del estado passengers. En lugar de declarar el target directamente, lo convertimos en un objeto con dos propiedades: el target hacia ticket y la condición cond apuntando a un guard llamado moreThanOnePassenger [01:00].

¿Para qué sirve un guard en una máquina de estado? Sirve para bloquear transiciones cuando no se cumple una regla. Por ejemplo, evitar que el usuario pase al ticket si la lista de pasajeros está vacía.

¿Dónde se declaran los guards dentro de la máquina?

Los guards viven al mismo nivel que los actions, dentro del segundo argumento del createMachine. Cada guard recibe el context como parámetro y debe retornar un booleano.

En este caso, la lógica es directa: revisar la longitud del arreglo de pasajeros.

js guards: { moreThanOnePassenger: (context) => { return context.passengers.length > 0; } }

Cuando refrescas el navegador y pulsas Ver mi ticket sin agregar a nadie, la máquina no responde. Eso es exactamente lo que queremos: la transición existe, pero el guard la frena. Al agregar un pasajero, la condición se cumple y el cambio de estado ocurre sin problemas.

¿Cómo mostrar los pasajeros del contexto en el ticket?

El segundo ajuste vive en el componente Ticket. La idea es recorrer el arreglo passengers que vive en el contexto y renderizar el nombre de cada persona que viaja.

Primero pasamos el contexto desde el StepLayout hacia Ticket usando state.context. No hace falta enviar todo el estado, solo lo que el componente necesita para pintar la información.

¿Cómo accedo al contexto de XState en un componente hijo? Pasa state.context como prop desde el componente padre que tiene acceso a la máquina, y úsalo dentro del hijo como cualquier objeto de React.

Dentro del componente, debajo del span que muestra el destino, hacemos un map sobre el arreglo de pasajeros. Cada iteración retorna un <p> con el nombre de la persona y un key basado en el índice para que React identifique correctamente cada elemento de la lista [02:30].

jsx {context.passengers.map((person, index) => (

<p key={index}>{person.name}</p> ))}

Al probarlo: comenzar, seleccionar país, agregar personas y pulsar Ver mi ticket. El ticket ahora muestra los nombres de quienes viajan a Colombia.

¿Qué retos puedes sumar para practicar máquinas de estado?

Antes de pasar al deploy, vale la pena estirar el proyecto. Practicar con extensiones reales fija los conceptos mucho mejor que repetir el mismo flujo.

Algunas ideas para sumar:

  • Ocultar el botón Continuar mientras no haya pasajeros agregados, usando la misma lógica del guard pero a nivel de UI.
  • Permitir que cada pasajero seleccione su silla, agregando un nuevo paso al flujo.
  • Capturar datos adicionales por pasajero, como documento o fecha de nacimiento.
  • Crear un paso de selección de equipaje con su propio estado y contexto.

Cada una de estas extensiones te obliga a tocar contexto, eventos, acciones y guards, que son justamente las piezas centrales de XState.

¿Cuál de estos retos vas a implementar primero en tu proyecto? Cuéntame en los comentarios cómo lo resolviste.