Introducción a XState

 

 

 

Una descripción general de la biblioteca de JavaScript de máquinas de estados finitos XState

En el pasado escribí sobre máquinas de estados finitos y mencioné XState . En esta publicación, quiero presentar esta popular biblioteca de JavaScript.

Las máquinas de estados finitos son una forma interesante de abordar estados complejos y cambios de estado y mantener su código libre de errores tanto como sea posible.

Así como modelamos proyectos de software utilizando diversas herramientas para ayudarnos a diseñarlos antes de construirlos, y utilizamos maquetas y herramientas UX para pensar en una UI antes de construirla, las máquinas de estados finitos nos ayudan a resolver transiciones de estados.

Los programas informáticos se basan en la transición de un estado a otro después de una entrada. Las cosas pueden salirse de control si no prestas mucha atención, y XState es una herramienta muy útil para ayudarnos a navegar por la complejidad del estado a medida que crece.

 

Instala XState usando npm:

npm install xstate

Luego, puede importarlo en su programa utilizando la sintaxis de módulos ES. Como mínimo, normalmente importa las funciones Machiney :interpret

import { Machine, interpret } from 'xstate'

En el navegador también puedes importarlo directamente desde un CDN:

script src="https://unpkg.com/xstate@4/dist/xstate.js"/script

y esto creará una variable XState global en el windowobjeto.

A continuación, puede definir una máquina de estados finitos mediante la Machinefunción de fábrica. Esta función acepta un objeto de configuración y devuelve una referencia a la máquina de estados recién creada:

const machine = Machine({})

En la configuración pasamos una idcadena que identifica la máquina de estados, la cadena de estado inicial. Aquí hay un ejemplo simple de semáforo:

const machine = Machine({ id: 'trafficlights', initial: 'green'})

También pasamos un statesobjeto que contiene los estados permitidos:

const machine = Machine({ id: 'trafficlights', initial: 'green', states: { green: { }, yellow: { }, red: { } }})

Aquí definí 3 estados: green yellowy red.

Para pasar de un estado a otro enviaremos un mensaje a la máquina, y esta sabrá qué hacer en función de la configuración que establezcamos.

Aquí configuramos para cambiar al yellowestado cuando estamos en el greenestado y obtenemos un TIMERevento:

const machine = Machine({ id: 'trafficlights', initial: 'green', states: { green: { on: { TIMER: 'yellow' } }, yellow: { }, red: { } }})

Lo llamé TIMERporque los semáforos normalmente tienen un temporizador simple que cambia el estado de las luces cada X segundos.

Ahora vamos a completar las otras 2 transiciones de estado: pasamos de amarillo a rojo, y de rojo a verde:

const machine = Machine({ id: 'trafficlights', initial: 'green', states: { green: { on: { TIMER: 'yellow' } }, yellow: { on: { TIMER: 'red' } }, red: { on: { TIMER: 'green' } } }})

¿Cómo desencadenamos una transición?

 

Puede obtener la representación de la cadena de estado inicial de la máquina utilizando:

machine.initialState.value //'green' in our case

y podemos cambiar a un nuevo estado usando el transition()método de machine(la instancia de máquina de estados devuelta por Machine()):

const currentState = machine.initialState.valueconst newState = machine.transition(currentState, 'TIMER')

Puede almacenar el nuevo objeto de estado en una variable y puede obtener su representación de cadena accediendo a la valuepropiedad:

const currentState = machine.initialState.valueconst newState = machine.transition(currentState, 'TIMER')console.log(newState.value)

Con este transition()método siempre hay que tener en cuenta el estado actual, lo que en mi opinión es un poco molesto. Sería genial si pudiéramos preguntarle a la máquina cuál es su estado actual.

Esto se hace creando un diagrama de estados, que en XState se llama servicio. Para ello, llamamos al interpret()método que importamos al xstatepasarle el objeto de máquina de estados y, luego, llamamos start()para iniciar el servicio:

const toggleService = interpret(machine).start()

Ahora podemos usar este send()método de servicio para recuperar el nuevo estado, sin tener que pasar el estado actual como tenemos que hacer con machine.transition():

const toggleService = interpret(machine).start()toggleService.send('TOGGLE')

Podemos almacenar el valor de retorno, que contendrá el nuevo estado:Te recomendamos Recetas faciles y rápidas

const newState = toggleService.send('TOGGLE')console.log(newState.value)

Esto es apenas el comienzo de XState.

Dado un estado, puedes saber qué desencadenará un cambio de estado utilizando su nextEventspropiedad, que devolverá una matriz.

Sí, porque desde un estado puedes pasar a múltiples estados dependiendo del disparador que obtengas.

En el caso de los semáforos, esto no es algo que sucederá, pero modelemos el ejemplo de las luces de la casa que teníamos en la publicación de máquinas de estados finitos:

Al entrar a la casa, puedes pulsar uno de los 2 pulsadores que tiene, p1 o p2. Al pulsar cualquiera de esos botones, se enciende la luz l1.

Imagina que esta es la luz de la entrada y que puedes quitarte la chaqueta. Una vez que hayas terminado, decides a qué habitación quieres ir (cocina o dormitorio, por ejemplo).

Si presionas el botón p1, l1 se apaga y l2 se enciende. En cambio, si presionas el botón p2, l1 se apaga y l3 se enciende.

Presionando otra vez cualquiera de los 2 botones, p1 o p2, la luz que esté encendida en ese momento se apagará y volveremos al estado inicial del sistema.

Aquí está nuestro objeto de máquina XState:

const machine = Machine({ id: 'roomlights', initial: 'nolights', states: { nolights: { on: { p1: 'l1', p2: 'l1' } }, l1: { on: { p1: 'l2', p2: 'l3' } }, l2: { on: { p1: 'nolights', p2: 'nolights' } }, l3: { on: { p1: 'nolights', p2: 'nolights' } }, }})

Ahora podemos crear un servicio y enviarle mensajes:

 

const toggleService = interpret(machine).start();toggleService.send('p1').value //'l1'toggleService.send('p1').value //'l2'toggleService.send('p1').value //'nolights'

Una cosa que nos falta aquí es cómo hacemos algo cuando cambiamos a un nuevo estado. Eso se hace a través de acciones, que definimos en un segundo parámetro de objeto que pasamos a la Machine()función de fábrica:

const machine = Machine({ id: 'roomlights', initial: 'nolights', states: { nolights: { on: { p1: { target: 'l1', actions: 'turnOnL1' }, p2: { target: 'l1', actions: 'turnOnL1' } } }, l1: { on: { p1: { target: 'l2', actions: 'turnOnL2' }, p2: { target: 'l3', actions: 'turnOnL3' } } }, l2: { on: { p1: { target: 'nolights', actions: ['turnOffAll'] }, p2: { target: 'nolights', actions: ['turnOffAll'] } } }, l3: { on: { p1: { target: 'nolights', actions: 'turnOffAll' }, p2: { target: 'nolights', actions: 'turnOffAll' } } }, }}, { actions: { turnOnL1: (context, event) = { console.log('turnOnL1') }, turnOnL2: (context, event) = { console.log('turnOnL2') }, turnOnL3: (context, event) = { console.log('turnOnL3') }, turnOffAll: (context, event) = { console.log('turnOffAll') } }})

Vea cómo ahora cada transición de estado definida en el objeto pasado onen lugar de ser solo una cadena, es un objeto con la targetpropiedad (donde pasamos la cadena que usamos antes) y también tenemos una actionspropiedad donde podemos configurar la acción a ejecutar.

Podemos ejecutar múltiples acciones pasando una matriz de cadenas en lugar de una cadena.

También puedes definir las acciones directamente en la actionspropiedad en lugar de “centralizarlas” en un objeto separado:

const machine = Machine({ id: 'roomlights', initial: 'nolights', states: { nolights: { on: { p1: { target: 'l1', actions: (context, event) = { console.log('turnOnL1') }, ...

Pero en este caso es útil ponerlos todos juntos porque acciones similares son provocadas por diferentes transiciones de estado.

Eso es todo por este tutorial. Te recomiendo que consultes la documentación de XState para obtener información más avanzada sobre el uso de XState, pero es un comienzo.

Tips para principiantes de JavaScript




Tal vez te puede interesar:

  1. Introducción a React
  2. Agregar evento de clic a los elementos DOM devueltos desde querySelectorAll
  3. Cómo cambiar el valor de un nodo DOM
  4. Cómo comprobar si un elemento DOM tiene una clase

Introducción a XState

En el pasado escribí sobre máquinas de estados finitos y mencioné XState . En esta publicación, quiero presentar esta popular biblioteca de JavaScript. Una

programar

es

2025-01-10

 

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Update cookies preferences