https://d226lax1qjow5r.cloudfront.net/blog/blogposts/using-web-components-in-a-react-application-dr/Blog_WebComponents_React_1200x600.png

Uso de componentes web en una aplicación React

Publicado el May 10, 2021

Tiempo de lectura: 14 minutos

En un entrada anteriorle mostramos cómo crear y publicar un Web Component.

Ahora es el momento de ver cómo utilizar una característica superior de Web Components:

Los componentes y widgets personalizados creados a partir de los estándares de Web Component funcionarán en todos los navegadores modernos y pueden utilizarse con cualquier biblioteca o framework JavaScript que funcione con HTML.

-WebComponents.org

En este post, veremos cómo los Web Components pueden integrarse en un entorno React en una aplicación React.

Cuenta API de Vonage

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.

Componentes web

En primer lugar, echemos un vistazo a los Web Components que se utilizarán en la aplicación.

Tenemos el componente de teclado del entrada anterior y un componente de lista de contactos. La lista de contactos puede guardar y cargar los nombres y números de teléfono de las personas a las que se llama desde el almacenamiento local del navegador. El componente web también puede emitir el número de teléfono de un contacto a la aplicación como un evento personalizado cuando se hace clic.

También existe un Componente Material Web que se mostrará una vez finalizada la llamada si el número no está ya en los contactos, para poder guardarlo.

Demonstration of Web Components in a React application making a call and saving the contact

Cuestiones

Para ver con qué obstáculos nos podemos encontrar al utilizar estos Web Components en una aplicación React, haremos referencia a Elementos personalizados en todas partes.

Han asumido la noble responsabilidad de "Garantizar que los frameworks y los elementos personalizados puedan ser mejores amigos 🍻".

Esto se consigue tomando frameworks/bibliotecas y sometiéndolos a varias pruebas e informando de los resultados sobre lo bien que se integran con Web Components.

¿Cómo queda React? A fecha de publicación de este post, estos son los resultados:

Results of using Web Components in a React application

No tiene buena pinta, pero podemos hacer que funcione. Echemos un vistazo a algunos de los problemas.

Una forma de pasar datos a tu Web Component es a través de propiedades:

<dwanes-keypad actionText="Call" cancelText="Hang up"></dwanes-keypad>

Cuando esto se hace en una React Application, los datos se convierten en cadenas. Así que pasar arrays (ie [1,2,3,4]) se convertirá en "1,2,3,4" y objetos (ie {"clave1":valor1, "clave2":valor2}) se convertirán en el temido "[objeto Objeto]".

Otro problema es cómo React maneja los eventos.

React tiene su SyntheticEvent que envuelve el evento nativo del navegador. Los hallazgos de Custom Elements Everywhere muestran que el SyntheticEvent de React "no puede escuchar eventos DOM procedentes de Custom Elements". También proporcionan una solución que veremos a continuación.

Soluciones

Ahora que sabemos lo que nos espera, trabajemos para superar estos problemas.

El plan es crear un componente básico React "App" y luego colocar nuestros Web Components dentro.

Hay dos maneras de escribir componentes en una aplicación React-como una Clase o Función.

Anteriormente, la necesidad de utilizar "Estado" en su componente era un factor determinante si se utilizaría un Componente de Clase sobre Componente de Función.

A continuación, en React 16.8, Hooks "Permiten utilizar el estado y otras características de React sin escribir una clase".

Así que para ser lo más exhaustivos posible en este post, incluiremos Web Components en aplicaciones React que se crean usando Class Components y Function Components con Hooks.

Como cada vez que se utilizan Web Components, deben incluirse en el proyecto. Puedes instalarlos o enlazarlos desde un CDN. (Esto fue cubierto en el entrada anterior.)

Para esta integración, utilizaremos una CDN.

En la <head> de public\index.html del código de ejemplo, encontrará:

<!-- Web Component polyfill -->
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@latest/webcomponents-loader.js"></script>

<!-- Load the Web Components -->
<script type="module" src="https://unpkg.com/@material/mwc-dialog@canary/mwc-dialog?module"></script>
<script type="module" src="https://unpkg.com/@dwane-vonage/dwanes-keypad@latest/dwanes-keypad.js?module"></script>
<script type="module" src="https://unpkg.com/@dwane-vonage/dwanes-contacts@latest/dwanes-contacts.js?module"></script>

Componente de clase

Puede encontrar el código de ejemplo en este Proyecto Glitch.

Nota: He remezclado la Plantilla Glitch React Starter.

Gestión de eventos

Para poder manejar los eventos provenientes de un Web Component, primero debemos obtener una referencia al mismo para que React sepa que existe y pueda agregar un listener de eventos. En la función src\App.js render, encontrarás algunas ref etiquetas:

<main>
    <section>
        <dwanes-keypad actionText="Call" cancelText="Hang up" ref="keypad"></dwanes-keypad>
        <div id="status">{this.state.callStatus}</div>
    </section>
    <dwanes-contacts ref="contacts"></dwanes-contacts>
</main>

<mwc-dialog id="dialog" heading="Contacts" ref="dialog">
    <p>Save <span id="number-to-save">{this.state.numberToSave}</span> to contacts?</p>
    <mwc-textfield
        id="text-field"
        minlength="3"
        maxlength="64"
        placeholder="First name"
        dialogInitialFocus
        required
        ref="firstName">
    </mwc-textfield>
    <mwc-button
        id="primary-action-button"
        slot="primaryAction"
        onClick={this.handleSaveContact}>
        Confirm
    </mwc-button>
    <mwc-button
        slot="secondaryAction"
        dialogAction="close">
        Cancel
    </mwc-button>
</mwc-dialog>

Las etiquetas ref permiten a React prestar atención a estos elementos.

En el componentDidMount() en src\App.jsse añaden los escuchadores de eventos a las referencias de elementos que acabamos de configurar. Este es el código:

this.refs.contacts.addEventListener('contacts-loaded', (event) => {
  contacts = event.detail.contacts;
  console.log('contacts-loaded: ', contacts);
});

this.refs.contacts.addEventListener('contact-selected', (event) => {
  this.refs.keypad.setDigits(event.detail.contact.phone);
});

this.refs.keypad.addEventListener('action-ended', () => {
  this.setState({callStatus: "Call has ended."});
  const contactFound = contacts.find(contact => contact.phone === this.state.numberToSave);
  if (contactFound){
    console.log('Number already in contacts')
  } else {
    this.refs.dialog.show();  
  }
});

this.refs.keypad.addEventListener('digits-sent', event => {
  if (event.detail.digits !== ""){
    this.refs.keypad.createAction();
    this.setState({callStatus: "Call is being made", numberToSave:event.detail.digits});
  } else {
    this.setState({callStatus: "Please enter a phone number."});
  }      
});

this.refs.keypad.addEventListener("digit-added", event => {
  console.log('digit-added: ', event.detail.digit);
});

Tratamiento de datos

Para nuestro componente de teclado, estamos pasando cadenas a las propiedades, por lo que no hay que hacer nada especial con esos datos en React.

Al guardar el contacto después de una llamada, los datos son un objeto:

{name:this.refs.firstName.value, phone:this.state.numberToSave}

Se pasa a un método del componente web de la lista de contactos:

this.refs.contacts.saveContact({name:this.refs.firstName.value, phone:this.state.numberToSave});

Si el componente de la lista de contactos no estuviera utilizando el objeto para manipular los datos y guardarlos en el Almacenamiento Local, podríamos pasar el objeto como:

this.refs.contacts.contactProp = {name:this.refs.firstName.value, phone:this.state.numberToSave};

Lo mismo ocurre con las matrices.

Nota importante La API utilizada para obtener la referencia a los Web Components se considera heredada y es "probable que se elimine en una de las futuras versiones". La documentación tiene más información y ofrece alternativas.

Mostremos una de las alternativas mencionadas.

Si estás usando React 16.3 o posterior, aquí está el Glitch Project código de ejemplo usando React.createRef().

Más o menos la misma idea, sólo que con más sintaxis. La referencia se crea con React.createRef() en el constructor:

this.contacts = React.createRef();

Adjúntelo al Web Component en el campo render:

<dwanes-contacts ref={this.contacts}></dwanes-contacts>

Añadir un receptor de eventos en componentDidMount():

this.contacts.current.addEventListener('contact-selected', (event) => {
  this.keypad.current.setDigits(event.detail.contact.phone);
});

Llamar al método de un Web Component:

this.contacts.current.saveContact({name:this.firstName.current.value, phone:this.state.numberToSave});

Pasar datos "ricos" al Web Component:

this.contacts.current.contactProp = {name:this.firstName.current.value, phone:this.state.numberToSave};

this.contacts.current.arrayProp = [value1, value2, value3];

Componente de función con ganchos

Ahora para la forma más nueva, a partir de React 16.8, que se puede utilizar para crear una referencia Web Component.

Gran parte del trabajo pesado se maneja con Hooks. Si se trata de manejar el estado, los efectos secundarios, o referencias DOM, probablemente hay un gancho que puede utilizar.

Si no, puedes crear un Gancho personalizado.

En este caso, uno de los Hooks que utilizaremos es useRef.

Se parecerá mucho al React.createRef() ejemplo.

Puede encontrar el código de ejemplo del componente de función con ganchos en este proyecto Glitch.

Gestión de eventos

Primero, necesitamos inicializar las referencias para cada Web Component. Eso se hace con este código:

const keypad = useRef(null);
const dialog = useRef(null);
const contactsEl = useRef(null);
const firstName = useRef(null);

null es el valor inicial de la referencia.

A continuación, en la sección de retorno, adjuntamos las referencias a los Web Components con la etiqueta ref={referenceName}.

Aquí está el código:

<main>
    <section>
      <dwanes-keypad actionText="Call" cancelText="Hang up" ref={keypad}></dwanes-keypad>
      <div id="status">{callStatus}</div>
    </section>
    <dwanes-contacts ref={contactsEl}></dwanes-contacts>
</main>

<mwc-dialog id="dialog" heading="Contacts" ref={dialog}>
    <p>Save <span id="number-to-save">{numberToSave}</span> to contacts?</p>
    <mwc-textfield
      id="text-field"
      minlength="3"
      maxlength="64"
      placeholder="First name"
      dialogInitialFocus
      required
      ref={firstName}>
    </mwc-textfield>
    <mwc-button
      id="primary-action-button"
      slot="primaryAction"
      onClick={handleSaveContact}>
      Confirm
    </mwc-button>
    <mwc-button
      slot="secondaryAction"
      dialogAction="close">
      Cancel
    </mwc-button>
</mwc-dialog>

Ahora vamos a añadir nuestros escuchadores de eventos.

Para ello, los envolveremos en un gancho useEffect:

useEffect(()=> {    
    contactsEl.current.addEventListener('contacts-loaded', handleContactsLoaded);
    contactsEl.current.addEventListener('contact-selected', handleContactSelected);
    keypad.current.addEventListener('digit-added', handleDigitAdded);
    keypad.current.addEventListener('digits-sent', handleDigitsSent);
    keypad.current.addEventListener('action-ended', handleActionEnded);
    
    return () => {
        contactsEl.current.removeEventListener('contacts-loaded', handleContactsLoaded);
        contactsEl.current.removeEventListener('contact-selected', handleContactSelected);
        keypad.current.removeEventListener('digit-added', handleDigitAdded);
        keypad.current.removeEventListener('digits-sent', handleDigitsSent);
        keypad.current.removeEventListener('action-ended', handleActionEnded);
    };
});

Tratamiento de datos

Como se mencionó anteriormente, esto se verá muy similar a la React.createRef() código de ejemplo.

Utilizar el método de un Web Component para enviar datos:

contactsEl.current.saveContact({name:firstName.current.value, phone:numberToSave});

Para pasar datos "ricos" al Web Component:

contactsEl.current.contactProp = {name:this.firstName.current.value, phone:this.state.numberToSave};

contactsEl.current.arrayProp = [value1, value2, value3];

Hacer una llamada

Ya tenemos nuestros Web Components conectados, ¿por qué no hacer una llamada con nuestra aplicación React?

Los siguientes pasos son una versión modificada y un resumen rápido de nuestro tutorial sobre cómo hacer una llamada de voz dentro de la aplicación. tutorial para utilizar nuestros Web Components.

Puede consultarlo para obtener más información.

Para conseguir que funcione requerirá:

Paso 1: Remezcla este proyecto Glitch.

Esto no sólo configurará la aplicación React con los Web Components, sino que también instalará el Client SDK de Nexmo que se utilizará para realizar la llamada telefónica.

Paso 2. Instale la herramienta Nexmo CLI: Instale la herramienta Nexmo CLI.

En tu terminal, escribe:

npm install nexmo-cli@beta -g

Obtén tu clave y secreto de API para desarrolladores de Vonage desde tu tablero.

Ejecute el siguiente comando en un terminal, sustituyendo api_key y api_secret por los suyos propios:

nexmo setup api_key api_secret

Paso 3: Crear una OCN.

Asegúrate de haber iniciado sesión en GitHub y luego vaya a https://gist.github.com .

Introduzca ncco.json en "Nombre de archivo con extensión".

Copia y pega el siguiente objeto JSON en el gist:

[
    {
        "action": "talk",
        "text": "Please wait while we connect you."
    },
    {
        "action": "connect",
        "endpoint": [
            {
                "type": "phone",
                "number": "PHONE_NUMBER"
            }
        ]
    }
]

Sustituya PHONE_NUMBER por su número de teléfono. Numbers Nexmo están en formato E.164, "+" y "-" no son válidos. Asegúrese de especificar el código de su país cuando introduzca su número, por ejemplo, US: 14155550100 y UK: 447700900001.

Pulse el botón Create secret gist botón .

Pulse el botón Raw botón .

Tome nota de la URL que aparece en su navegador, la utilizará en el siguiente paso.

Paso 4. Crear una aplicación Nexmo Crear una aplicación Nexmo

Cree el directorio de su proyecto si aún no lo ha hecho.

mkdir web-components-react

Cambie al directorio del proyecto.

cd web-components-react

Cree una aplicación Nexmo copiando y pegando el siguiente comando en el terminal:

Asegúrese de cambiar el valor de --voice-answer-url sustituyendo GIST-URL por la URL gist del paso anterior.

nexmo app:create "App to Phone Tutorial" --capabilities=voice --keyfile=private.key --voice-event-url=https://example.com/ --voice-answer-url=GIST-URL

Se creará un archivo llamado .nexmo-app en el directorio del proyecto y contiene el ID de la aplicación Nexmo recién creada y la clave privada. También se crea un archivo de clave privada llamado private.key también se crea.

Anote el ID de la solicitud, ya que lo necesitará en el futuro.

Paso 5. Crear un usuario Crear un usuario

Crea un usuario llamado Alice con el siguiente comando usando el Nexmo CLI:

nexmo user:create name="Alice"

Esto devolverá un ID de usuario similar al siguiente:

User created: USR-aaaaaaaa-bbbb-cccc-dddd-0123456789ab

Paso 6. Generar un JWT Generar un JWT

Genere un JWT utilizando la CLI de Nexmo ejecutando el siguiente comando, pero recuerde sustituir la variable APP_ID por tu propio valor:

nexmo jwt:generate ./private.key exp=$(($(date +%s)+21600)) acl='{"paths":{"/*/users/**":{},"/*/conversations/**":{},"/*/sessions/**":{},"/*/devices/**":{},"/*/image/**":{},"/*/media/**":{},"/*/push/**":{},"/*/knocking/**":{},"/*/legs/**":{}}}' sub=Alice application_id=APP_ID

El JWT generado será válido durante las próximas 6 horas.

Copia y pega el JWT en el src/App.js Glitch archivo de proyecto donde dice "PEGAR ALICE JWT AQUÍ".

Paso 7: Llámate a ti mismo

Marque su número en el teclado y pulse LLAMAR.

Si todo ha funcionado correctamente, tu teléfono debería recibir una llamada. Cuando finalice la llamada, aparecerá un cuadro de diálogo preguntándote si deseas guardar el número.

Nota: Dado que el NCCO está codificado con su número de teléfono, independientemente de lo que se haya escrito con el teclado, se llamará a su número. Para generar dinámicamente el NCCO, tendrá que hacerse en el back-end.

Bueno, ¡ya está! Ya hemos utilizado Web Components en una aplicación React.

¿Funcionó? ¿Estuvo bien?

Como ocurre con todo en codificación, hay múltiples formas de hacer las cosas.

Otra forma, que he encontrado en React y Stencil JS es envolver tus Web Components en React Components.

¿Lo has hecho así o quizás de otra manera? Me gustaría mucho que nos lo contaras o que nos hicieras cualquier otro comentario o pregunta en nuestro Canal Slack de la Comunidad.

Compartir:

https://a.storyblok.com/f/270183/384x384/1a06993970/dwanehemmings.png
Dwane HemmingsPromotor del desarrollo de JavaScript