https://d226lax1qjow5r.cloudfront.net/blog/blogposts/learn-and-apply-xstate-with-vonage-video/Blog_XState_VideoAPI_1200x600.png

Aprende y aplica XState con Vonage Video

Publicado el May 4, 2021

Tiempo de lectura: 14 minutos

En los últimos meses, he oído hablar más de máquinas de estado utilizadas para el desarrollo front-end. La idea de una máquina de estados es que sólo tiene un número finito de estados y sólo puede estar en un estado en un momento dado. Conceptualmente, esto tiene mucho sentido para el desarrollo de aplicaciones: sólo hay un número determinado de estados disponibles.

El concepto de máquinas de estados y diagramas de estados no es nuevo, y tampoco tiene sus raíces en el desarrollo front-end. Es un modelo matemático que se utiliza en muchas cosas a nuestro alrededor. Por ejemplo, una luz puede ser OFF o ON. Se puede describir cualquier cosa con una máquina de estados, aunque éste sea un ejemplo sencillo.

On and off states of lightbulbOn and off states of lightbulb

Introducción a XState

Utilizar una máquina de estados en el desarrollo front-end se ha vuelto mucho menos complicado con la creación del paquete XState. XState nos ayuda a definir máquinas de estados, crear eventos y efectos, y controlar todo el flujo de la aplicación. XState utiliza métodos y objetos JavaScript para describir la máquina de estados.

El ejemplo de la bombilla anterior se escribiría de la siguiente manera:

const lightBulb = Machine({
  id: 'lightBulb',
  initial: 'off',
  states: {
    off: {
      on: {
        TURN_ON: 'on'
      }
    },
    on: {
      on: {
        TURN_OFF: 'off',
      }
    }
  }
});

La máquina de estados definida aquí muestra los dos estados de la bombilla, OFF y ONy las transiciones desde los sucesos TURN_ON y TURN_OFF.

El objeto en sí no es muy complejo de leer, pero a medida que la máquina de estados crece en complejidad, puede ser más difícil de entender. XState ha creado una herramienta para ayudar con esto - el visualizador XState.

El uso del visualizador XState ayuda a ver cómo funcionan las máquinas de estados y son interactivas, por lo que jugar con ellas es divertido. Si quieres echar un vistazo al código de la máquina, también puedes hacer clic en el botón de código para echar un vistazo.

Creación de un gráfico de estado de Video de Vonage

Cuando me propuse aprender máquinas de estado y XState, mi objetivo general era crear una aplicación similar a Google Meet utilizando Vonage Video. La aplicación permitiría a un usuario crear una sala de reuniones, compartir la URL, y tener una reunión con múltiples flujos. Para llegar a ese punto, tuve que aprender algunos de los diversos conceptos para los gráficos de estado y cómo representarlos en XState.

Pensar en los posibles estados de aplicación no es tarea fácil. Hay muchas posibilidades que explorar y, en última instancia, encontrar la solución adecuada requiere algo de ensayo y error.

El resto de este artículo cubrirá algunos Conceptos básicos y construirá una visualización de gráfico de estado que imitará la eventual máquina de estado. También proporcionaré algunos enlaces y recursos adicionales para que puedas explorar por tu cuenta.

Estados y nodos de estado

A estado es una representación de una máquina en un momento dado. Este momento puede definirse y luego convertirse en un nodo de estado en XState, capturado como una configuración.

En mi aplicación Vonage Video, hay un par de posibles soluciones diferentes para esto, pero he descubierto que describir los estados en los términos más simples posibles es la mejor manera de llegar a un resultado útil.

Creación de una máquina utiliza el siguiente patrón:

const machine = Machine(state_nodes, options)

Con una máquina de estados de Video en mente, hay dos estados compuestos - connected y disconnected.

Dos nodos de estado puede parecer demasiado simplificado, pero sólo hay dos estados después de un poco de ensayo y error. Sin embargo, cada uno de estos estados es más complejo que un nodo atómico (sin hijos). En lugar de crear todos los estados posibles en el nivel superior, XState nos ayuda a organizarnos con nodos de estado jerárquicos y paralelos.

Nodos de estado jerárquicos

XState proporciona la opción de crear estados anidados llamados hierarchical nodos de estado. Cuando arrancamos la máquina por primera vez, podemos ponerlo en idle primero, ya que la máquina estará lista pero sin hacer nada. ¿Por qué no hacer otro nodo de estado atómico de nivel superior?

Añadir estados al nivel superior se denomina "explosión de estados" y es un efecto secundario típico de las máquinas de estados finitos. Dado que Vonage Video sigue siendo técnicamente disconnectedanidamiento idle tiene sentido, ya que el Video está tanto desconectado como inactivo. Otro subestado de disconnected subestado debería ser ready. El estado disconnected.ready ocurriría justo antes de pasar al estado connected estado . La máquina de estados también tiene un nodo de estado entre idle y ready para que todo esté listo. Este estado intermedio puede llamarse init fase.

La máquina de estados ahora se vería así:

Deberías notar que actualmente no tenemos una manera de movernos entre los dos nodos. Cubriremos los eventos y acciones en un momento.

Nodos de estado paralelos

A parallel nodo de estado permite a la aplicación estar en todos sus subestados al mismo tiempo. La máquina de estados de Vonage Video depende en gran medida de los eventos, por lo que necesitamos gestionar varios estados a la vez.

Para especificar que un nodo de estado es paralelo, utilizamos type:parallel en la configuración. Tras la transición a connectedse producirán tres estados paralelos - session, publishery subscribers. Cada uno de estos estados configurará eventos y escuchas de eventos para controlar las respuestas del servicio de Vonage Video.

La visualización resultante tiene este aspecto:

Con estos estados primarios, podemos controlar lo que muestra nuestra aplicación en determinados momentos. Actualmente, sin embargo, no podemos movernos entre estados. Echemos un vistazo a los eventos y transiciones.

Eventos y transiciones

Dado que un nodo de estado es sólo la configuración de un estado individual, su inherentemente no es una manera de pasar de un estado a otro sin declarar que en el nodo de estado.

Cada nodo espera el envío de un evento a transición al siguiente estado. En el ejemplo de la bombilla, el evento TURN_ON indica a la máquina que pase a on.

Las transiciones sólo se producen entre nodos de nivel superior y dentro de nodos jerárquicos. Los nodos paralelos no pueden realizar transiciones entre sí. Para nuestra aplicación Vonage Video, esto significa lo siguiente:

  1. Cuando la página esté lista, podemos enviar un START evento. Este evento cambiará el estado a disconnected.init.

  2. El estado disconnected.init saldrá una vez que el evento VIDEO_ELEMENT_CREATED evento se haya disparado.

  3. Una vez que alcancemos disconnected.readypodemos permitir que el usuario se conecte, enviando el evento CONNECT y pasar a connected.

  4. Si la aplicación pasaba el evento DISCONNECT evento, la máquina de estados se desconectaría.

Declarar un evento y una transición en XState utiliza el siguiente patrón:

on: {
  EVENT_DESCRIPTOR: 'nextState'
}

También puedes añadir acciones específicas a la transición. Le recomiendo que lea las secciones sobre transiciones internas y externas en la documentación. Cubren con gran detalle los distintos tipos de transiciones posibles.

Transiciones vigiladas

Habrás notado que una de las transiciones tiene un nodo cond en la transición. Este condicional es lo que se llama una guarded transición. Las transiciones guardadas ayudan a proteger la máquina de moverse a un estado que no está permitido basado en ciertas condiciones. En este caso, no quiero pasar a ready hasta que el elemento token y el elemento Video hayan sido creados.

on: {
  'VIDEO_ELEMENT_CREATED': {
    target: 'ready',
    cond: 'checkToken'
  }
}

La condición de guardia checkToken es una referencia con nombre al objeto guards en el parámetro options enviado a machine:

const video = Machine(
  state_nodes,{
  guards: {
    checkToken: () => true
  }
});

Contexto

Para ser más útil a una aplicación, nuestra máquina de estados necesitará un estado de vida más larga, llamado extended stateo context. El objeto de contexto se actualiza utilizando varios efectos con el assign() método.

Hablando de acciones, pongámonos con ellas ahora y terminemos el resto del esqueleto.

Acciones y servicios

Hay lo que se conoce como "efectos secundarios" en las máquinas de estado que XState clasifica en una de dos categorías:

  1. "Disparar y olvidar" - cuando el efecto no envía eventos

  2. Invocados - cuando se requiere el envío de eventos

Acciones

Acciones son efectos individuales y tienden a ser uno de los efectos más comunes en la máquina de estados de Video. Puedes usar acciones cuando entras o sales de un nodo, o durante una transición. Entender el orden de las acciones es increíblemente importante.

Un excelente recurso para aprender el orden de las acciones (y todos los temas sobre XState) es un Video publicado por @kyleshvlin en Egghead.io. Me ayudó a entender cómo se disparan las acciones.

La mayor parte de las acciones de esta máquina giran en torno a la actualización del contexto como una transición. Cuando se llaman eventos, podemos utilizar la acción para ejecutar el método assign() método:

on: {
  'SOME_EVENT': {
      actions: assign({'someContext': (ctx, e) => e.someValue})
  } 
}

Servicios

Los servicios invocados son el otro efecto principal en la máquina de estado de Video. Para usar promesas y escuchadores de eventos, necesitas invocar un servicio. Este concepto fue, con mucho, el más difícil de entender para mí. La principal dificultad que tuve que trabajar fue entender que un servicio invocado se detiene cuando el estado sale. Si haces la transición demasiado rápido, tu promesa o callback desaparecerá.

Hay dos servicios principales que estoy usando en la máquina de estado de Video, promesas y callbacks. El servicio invoke promises servicio permite a la máquina de estado utilizar una promesa para resolver o rechazar y luego actuar en consecuencia. He utilizado esto para interactuar con el servidor de forma asíncrona y luego actualizar el contexto cuando se haya completado.

La firma de función de las promesas invocadas tiene el siguiente aspecto:

src: (context, event) => new Promise((resolve, reject) => {
  if (event.error) reject('Rejected')
  resolve('Resolved')
}),
onDone: {/*success transition*/}
onError: {/*error transition*/}

La segunda parte, y probablemente la más importante de esta máquina, es el invoked callbacks. La arquitectura de Vonage Video se basa en gran medida en eventos y escuchadores de eventos. En una máquina de estados, estos se configuran a través de un callback. Déjame mostrarte un ejemplo:

invoke: {
  id: 'initPublisher',
  src: (ctx) => (cb) => {
    let publisher = initPublisher(pubOptions);
    publisher.on('videoElementCreated', (e) => {
      cb({ type: 'VIDEO_ELEMENT_CREATED', publisher: publisher })
    })
    return () => publisher.off('videoElementCreated');
  }
}

La firma de función de una llamada de retorno invocada tiene el siguiente aspecto:

src: (context, event) => (callback, onReceive) => {
  
  callback('EVENT');
  onReceive(event) => { callback('OTHER_EVENT') };

  return () => cleanup()
};

Usando acciones y servicios, nuestra máquina de estado de Vonage Video ahora tiene el siguiente aspecto:

No se olvide de hacer clic alrededor, y haga clic en el code para ver la configuración de la máquina de estados.

Recursos y recapitulación

Vale, has llegado hasta aquí.

I'm proud of you!I'm proud of you!

Si es la primera vez que echas un vistazo a XState, hay mucho que asimilar de golpe. Todavía estoy explorando y aprendiendo nuevas formas de hacer las cosas. Este post es sólo una pequeña parte de lo que hay ahí fuera. Como usted está aprendiendo - aquí hay algunos grandes recursos que usted querrá comprobar hacia fuera

No dudes en ponerte en contacto con nosotros si tienes alguna pregunta sobre XState, ¡y podremos aprender algo nuevo juntos!

Compartir:

https://a.storyblok.com/f/270183/384x384/444c073b5e/kellyjandrews.png
Kelly J AndrewsAntiguo miembro del equipo

Kelly J Andrews es desarrolladora de Nexmo y lleva más de 30 años jugando con los ordenadores. Utilizó BASIC por primera vez a los 5 años.

No fue hasta que creó su primera página web en 1997 y probó JavaScript por primera vez cuando encontró su verdadera vocación. Kelly lucha ahora por JavaScript, el código comprobable y la entrega rápida.

Se le puede encontrar cantando karaoke, haciendo magia o animando a los Cubs y a los Fighting Irish.