@composi/runtime:

Unión Etiquetada

Mensajes con Acciones

Cuando tratábamos de cómo usar acciones en la función update, el uso fue muy similar al de Redux. Pasábamos mensajes, los cuáles son objetos on un tipo y datos opcionales. Seguro que Redux requiere mucho más boilerplate (código repetivo). Aún así, hay una ligera desconexión entre lo que está haciendo un evento de la vista y lo que sucedió dentro de la función udpate. @composi/core ofrece una solución a esta desconexión por medio de uniones etiquetadas. Proporciona uniones etiquetadas con su función union. Para usarlo, primero tienes que importarlo a su proyecto:

import { h, render, run, union } from '@composi/core'

Una unión etiquetada es un objeto que connecta un mensaje con una acción en la función update. Hacer esto nos permite invocarlos desde la vista. En nuestro ejemplo anterior, usamos cadenas con guión como tipo de mensaje: "añadir-ítem", "eliminar-ítem", etc. Ahora vamos a cambiar esas a nombres sin quiones que podemos usar como función en esta form: "AnadirItem", "EliminarItem". Usaremos estos como los valores para crear una unión etiquetada. Antes de la versión 1.4.0 se tenía que incluir estos argumentos en una matriz, incluso si es un solo valor. Ahora se proveyen los argumentos separados con comas:

// Creamos una unión etiquetada y la quardamos en el variable Msj (Mensaje):
const Msj = new union('AnadirItem', 'EliminarItem')

Lo anterior crea una unión etiquetada que podemos usar con nuestras acciones de update. Esto nos permitirá llamar esas acciones de update directamente en nuestra vista:

Aquí mostramos cómo usar la unión etiquetada en la vista:

const Msj = new union('AnadirItem', 'EliminarItem')
// Destructurar los métodos de la unión:
const {AnadirItem, EliminarItem} = Msj
// Enviar esos mensajes con algunos datos:
send(AnadirItem, 'Sandía')
send(EliminarItem, 132)

Y aquí usamos los mensajes en el marcado:

// Anadir un ítem:
<button class='añadir-item' onclick={() => send(AnadirItem, valorDelInput)}>Anadir</button>
// Eliminar un ítem:
<button class="eliminar-item" onclick={() => send(EliminarItem, ítem.key)}>X</button>

También prodríamos invocar los métodos destructurados al enviarlos, pasádolos algunos datos como sus argumentos:

// Anadir un ítem:
<button class='añadir-item' onclick={() => send(AnadirItem(valorDelInput))}>Anadir</button>
// Eliminar un ítem:
<button class="eliminar-item" onclick={() => send(EliminarItem(ítem.key))}>X</button>

Compara eso con la forma previa de enviar mensajes:

// Anadir un ítem:
<button class='añadir-ítem' onclick={() => send({tipo: 'añadir-ítem', datos: valorDeInput})}>Anadir</button>
// Para eliminar un ítem:
<button class="eliminarItem" onclick={() => send({tipo: 'delete-ítem', datos: ítem.clave})}>X</button>

Usar las uniones etiquetadas resulta que el código sea más conciso y fácil de leer y entender.

Juegos de Mensajes y Acciones

Una unión etiquetada tiene un método llamado match. Éste toma dos argumentos: un mensaje y un objetos de metodos al que el mensaje debe corresponder. El método match chequea el tipo del mesaje para ver si existe cómo método in el objecto. Si sí existe, match lo invoca con cualesquier datos que acompañen el mensaje.

Ahora, cuando configuramos la función update, verificamos que el mensaje enviado coincida con los valores de la unión etiquetada. Lo hacemos utilizando el método match en la unión etiquetada. Date cuenta que no tenmos que chequar el tipo de la etiqueta: msg.tipo, solo el valor directo de la etiqueta. Además, no necesitamos acceder a los datos del mensaje con msg.datos. En su lugar, está directamente disponible para el callback de acción como su argumento. Ya que estamos usando el método match de la unión etiquetada, no necesitamos una sentencia de switch(switch, case, break). Esto resulta en código más sencillo para las acciones:

function actions(estado, msg) {
  // Usar clone para clonar el estado:
  const estadoPrevio = clone(estado)
  return Msj.match(msg, {
    // Chequear la etiqueta del mensaje:
    'añadirItem': value => {
      estadoPrevio.frutas.push({ clave: estado.newKey++, value })
      return estadoPrevio
    },
    // Chequear la etiqueta del mensaje:
    'eliminarItem': clave => {
      estadoPrevio.frutas = estadoPrevio.frutas.filter(ítem => ítem.clave != clave)
      return estadoPrevio
    }
  })
}

// Sin la unión etiquetada:
function actions(estado, msg) {
  // Usar clone para clonar el estado:
  const estadoPrevio = clone(estado)
  switch (msg.tipo) {
    case 'añadir-ítem':

      estadoPrevio.frutas.push({ clave: estadoPrevio.newKey++, msg.valorDeInput })
      return estadoPrevio
      break
    case 'eliminar-ítem':
      estadoPrevio.frutas = estadoPrevio.frutas.filter(ítem => ítem.clave != msg.clave)
      return estadoPrevio
      break
  }
}

El ejemplo anterior debería dejar claro por qué usar una unión etiquetada. Hace el código más limpio y más fácil de comprender. El uso de una unión etiquetada en la vista aclara la intención del evento. La etiqueta implica que invocará una acción correspondiente de update. Sin uniones etiquetadas, los eventos en nuestra vista disvían los objetos del mensaje a un destino no especificado. Confiamos en nuestra comprensión de cómo funciona el entorno de ejecución para asumir que serán interceptados por acciones de update. Las uniones etiquetadas hacen que la intención de los eventos de la vista sea más clara y las acciones de udpate más compactas.

Sigue un ejemplo vivo de cómo usar una unión etiquetada para definir las acciones y enviarlas desde la vista.

See the Pen @composi/core + runtime + unión by Robert Biggs (@rbiggs) on CodePen.