Tipos de transiciones en máquinas de estados
Resumen
Las transiciones en máquinas de estados son el mecanismo que mueve tu aplicación de un estado a otro cuando ocurre un evento. Si trabajas con XState o cualquier librería de state machines, entender los tipos de transiciones te permite modelar flujos complejos sin que tu código se vuelva un nudo de condicionales.
¿Cuál es la diferencia entre evento y transición?
Antes de revisar los tipos, necesitas tener clara una distinción que muchos desarrolladores mezclan al inicio. El evento es la acción que dispara el cambio, por ejemplo un clic en un botón. La transición es el proceso de pasar de un estado a otro, y se activa gracias a ese evento.
Dicho de otra forma: el evento es el gatillo, la transición es el viaje. La función transition recibe dos argumentos clave: el estado actual desde el que parte y el evento que la dispara.
¿Qué es una transición en una máquina de estados? Es el paso controlado de un estado a otro, activado por un evento. Define qué puede ocurrir y bajo qué condiciones cambia tu sistema.
¿Qué tipos de transiciones existen y cuándo usarlas?
No todas las transiciones funcionan igual. Cada tipo resuelve un problema distinto en el modelado de flujos.
Transiciones habilitadas y prohibidas
Una transición habilitada es la que sí va a ocurrir cuando la llames. Si estás en el estado inicial y sabes que puedes ejecutar el evento start, esa transición está habilitada y se ejecutará sin problema.
Una transición prohibida es lo contrario: existe el evento, pero decides bloquearlo en cierto estado. Imagina una máquina que registra logs en cada estado, pero hay uno donde no quieres que se loguee nada. En ese estado, el evento de log tendrá un valor undefined en lugar de un target o una acción. El evento existe en la máquina, pero ahí simplemente no hace nada.
Wildcards: el comodín del asterisco
Los wildcards funcionan parecido a las máscaras de IP. El operador es el asterisco (*) y se coloca en lugar del nombre del evento dentro de un estado.
¿Qué significa esto? Que cualquier evento que se dispare en ese estado entrará por el wildcard y ejecutará la transición asociada. Si el asterisco tiene un target hacia el siguiente estado, no importa qué evento llegue: el cambio de estado ocurre igual.
¿Para qué sirve un wildcard en XState? Sirve para capturar cualquier evento dentro de un estado sin tener que listarlos uno por uno. Útil cuando quieres una transición genérica de salida.
¿Cómo funcionan las transiciones sin eventos y las protegidas?
Aquí entran dos conceptos que suelen trabajar juntos y resuelven decisiones intermedias en tu flujo.
Transiciones sin eventos
Una transición sin evento se ejecuta sola al entrar a un estado, sin esperar que el usuario haga nada. Piensa en este caso: estás en un estado inicial donde llenas datos para saber si eres padre o hijo. Necesitas ramificar el flujo, pero no quieres que el usuario active otro botón.
La solución es un estado intermedio que, al recibir el control, ejecuta automáticamente una transición. Y para decidir hacia dónde ir, te apoyas en el siguiente tipo.
Transiciones protegidas o guards
Las transiciones protegidas (también llamadas guards) son transiciones que tienen una condición para ejecutarse. En el ejemplo del estado intermedio, el guard revisa: si es padre, termina la transición hacia el estado padre; si es hijo, la termina hacia el estado hijo.
Los guards también sirven para validaciones de negocio. En un sistema de reservas, puedes usar un guard para que si no se ha seleccionado un pasajero, no se permita avanzar al siguiente estado. Así la regla queda dentro de la máquina y no dispersa en el código de la UI.
¿Qué es un guard en una máquina de estados? Es una condición booleana que decide si una transición se ejecuta o no. Sin que el guard se cumpla, el estado no cambia.
¿Qué son las transiciones atrasadas y cuándo conviene usarlas?
Las transiciones atrasadas son las que se ejecutan después de esperar un tiempo determinado. Si recuerdas el demo del proyecto, cuando llegabas a la pantalla del ticket, la máquina volvía sola al estado inicial. Eso es una transición atrasada en acción.
Cómo se declara una transición atrasada
En lugar del objeto on, usas after y le pasas el tiempo en milisegundos. La máquina entiende que, una vez dentro de ese estado, si pasan esos milisegundos sin actividad, debe ejecutar la transición.
on: dispara la transición con un evento.after: dispara la transición tras un tiempo en milisegundos.- Combina ambas si quieres que el usuario pueda actuar antes del timeout.
Un caso típico: una máquina que modela un checkout. Cuando se imprime el ticket, defines una transición atrasada de 10 minutos. Si el usuario no ejecuta ninguna otra acción en ese tiempo, la máquina se resetea automáticamente y queda lista para la siguiente transacción.
Con estos cinco tipos (habilitadas, prohibidas, wildcards, sin eventos, protegidas y atrasadas) ya tienes la base teórica para modelar prácticamente cualquier flujo. ¿Qué tipo de transición usarías primero en tu proyecto actual? Cuéntalo en los comentarios.