https://d226lax1qjow5r.cloudfront.net/blog/blogposts/chat-app-with-react-and-nexmo-dr/React_Nexmo.png

Crear una aplicación de chat con React y Nexmo

Publicado el May 10, 2021

Tiempo de lectura: 4 minutos

Una de las formas más sencillas de comunicarse en línea también se encuentra entre las más sencillas de añadir a su sitio web, utilizando React y las herramientas JavaScript del lado del cliente de Nexmo. Una aplicación de chat puede proporcionar servicio al cliente, facilitar la colaboración en un proyecto, o le permiten ponerse al día con los amigos. Y buenas noticias: si has seguido nuestro anterior tutorial completo de React y Expressya tienes la mayoría de las piezas que necesitas para crear una.

Requisitos previos

Para abreviar, supongamos que ha seguido el tutorial tutorial de React y Express.

Al igual que en el código de ejemplo de pila completa, el código de ejemplo de este tutorial satisfará este último requisito utilizando Glitch.

Si utiliza la Aplicación Nexmo que creó para su aplicación de pila completa, puede generar uno o dos ID de Conversación y copiarlos en un archivo de texto para utilizarlos en el siguiente paso. Si prefiere crear una nueva aplicación para este proyecto, puede crear las conversaciones desde la línea de comandos.

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.

Añadir salas de chat a NexmoApp

Para mantener la funcionalidad separada, añada un nuevo componente a su aplicación React en client/src/Chatroom.js. Por ahora, puedes dejarlo casi vacío:

import React from 'react';
import styles from './Chatroom.css';

class Chatroom extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
};

export default Chatroom;

También puede añadir su archivo CSS, y rellenarlo con el estilo que desee en cualquier momento.

Si abre NexmoApp.js verá un par de referencias al componente Conversation componente. El componente Chatroom se utilizará de forma similar, así que puede sustituirlas por referencias a Chatroom. Deberían estar en el importy en la función render función .

En rendercambie ligeramente la etiqueta Chatroom para que en lugar de pasar invitespase chats:

  render() {
    return (
      <div className="nexmo">
        <User onUpdate={this.userUpdated}/>
        <Chatroom app={this.state.app} loggedIn={!!this.state.token} chats={this.state.chats} />
      </div>
    );
  }

Las demás propiedades pasadas a Chatroom ya existen como parte de la autenticación del usuario y el inicio de sesión, pero chats no forma parte del estado. Para esta aplicación sencilla, codifica las Conversaciones que creaste anteriormente en el estado inicial del componente. Puedes darles los nombres que quieras para diferenciarlas para los usuarios finales:

constructor(props) {
    super(props);
    this.state = {
      chats: [
        {
          id: 'CON-123e456c-5ff0-789c-8a11-e4a56a7b8c90',
          name: 'nice chat'
        },
        {
          id: 'CON-2c34ecec-f567-8e90-bf1d-23e4567e890a',
          name: 'serious business'
        }
      ]
    };
    
    this.login = this.login.bind(this);
    this.getJWT = this.getJWT.bind(this);
    this.userUpdated = this.userUpdated.bind(this);
  }

Dado que esta aplicación no gestiona conversaciones ni invitaciones, también puedes eliminar el código en login para obtener Conversaciones. Esto deja esa función sólo iniciar sesión y almacenar una referencia a la aplicación Nexmo:

  login() {
    let nexmo = new nexmoClient();
    nexmo.createSession(this.state.token).then(app => {
      this.setState({
        app: app
      });
    });
  }

Un chat sencillo

Puede dejar todo el User código del componente. Continuará haciendo lo mismo, creando un nuevo usuario u ofreciendo una lista de usuarios existentes. Una vez que el usuario ha iniciado sesión, puede continuar con el chat.

El componente Chatroom contendrá dos estados: la elección de una sala de chat y la propia sala de chat. Entre bastidores, una sala de chat no es más que una Conversación Nexmopor lo que parte del código de este componente será similar al del componente Conversation de Nexmo. Para empezar, puedes hacer un stub out de las funciones y condicionales necesarias para ambos estados:

import React from 'react';
import styles from './Chatroom.css';

class Chatroom extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      messages: []
    };
    
    this.joinConversation = this.joinConversation.bind(this);
    this.onMessage = this.onMessage.bind(this);
    this.setInput = this.setInput.bind(this);
    this.sendInput = this.sendInput.bind(this);
  }
  
  joinConversation(evt) {}
  
  onMessage(sender, message) {}
  
  setInput(evt) {}
  
  sendInput() {}
  
  render() {
    if (this.state.conversation) {
      
    } else {
     
    }
  }
};

export default Chatroom;

Unirse a un chat

Dado que el usuario dispone de un conjunto finito de salas de chat predefinidas entre las que elegir, un desplegable le permitirá seleccionar una fácilmente. Si sólo tuviera un chat, podría prescindir por completo de esta interfaz. Para crear un menú desplegable para las dos salas de chat codificadas en NexmoAppse realiza un bucle sobre el array para construir un conjunto de optiony añádalos como hijos de select:

  render() {
    if (this.state.conversation) {
    } else {
      let opts = [<option key="0">-</option>];
      this.props.chats.forEach(chat => {
        opts.push(<option key={chat.id} value={chat.id}>{chat.name}</option>);
      });
      
      return (
        <div className="conversation">
          <label>Choose a chat to join: 
            <select onChange={evt => this.joinConversation(evt)}>
              {opts}
            </select>
          </label>
        </div>
      );
    }
  }

Cuando cambia el valor del desplegable, joinConversation se activa. El controlador joinConversation obtendrá la conversación elegida por su ID de la aplicación Nexmo y se unirá a ella. También almacena una referencia a ella y le asigna otro controlador de eventos para los mensajes entrantes:

  joinConversation(evt) {
    let select = evt.target;
    this.props.app.getConversation(select.value).then(conv => {
      conv.on('text', this.onMessage);
      conv.join();
      this.setState({
        conversation: conv
      });
    });
  }

El gestor onMessage se activa cada vez que hay un nuevo evento text en la conversación activa. Recibe información sobre el miembro de la conversación que ha disparado el evento y sobre el propio objeto del evento. Para un chat simple puedes descartar la mayor parte de esa información y guardar sólo el ID, el nombre del usuario y el texto del mensaje. Esta información puede ser concatenada en una lista de mensajes almacenados en el estado:

onMessage(sender, message) {
    let newMessages = this.state.messages.concat({
      key: message.id,
      sender: sender.display_name,
      text: message.body.text
    });
    this.setState({
      messages: newMessages
    });
  }

Si estuvieras diseñando incluso un chat simple como este para uso en producción, querrías planear mover los mensajes antiguos a un objeto de almacenamiento diferente después de algún tiempo. Con cualquier cantidad significativa de tráfico, una sola matriz para contener todos los mensajes inevitablemente causará problemas.

Envío de mensajes

Una vez que el usuario ha iniciado sesión y se ha unido a un chat, querrá enviar y recibir mensajes. Esto significa que quieres mostrar una interfaz de usuario con, al menos, un área para ver los mensajes y un campo de entrada para el texto. El JSX para esto rellena la otra rama de la condicional principal en render. Recorre el array de mensajes y muestra todo lo recibido desde que el usuario se unió al chat. Debajo de eso, proporciona un textarea y un botón que establecen el texto recién introducido y lo envían, respectivamente:

  render() {
    if (this.state.conversation) {
      let messagePane = [];
      
      if (this.state.messages.length) {
        this.state.messages.forEach(msg => {
          messagePane.push(<p key={msg.key} className="message"><b>{msg.sender}:</b>{msg.text}</p>);
        });
      }
      
      return (
        <div className="conversation">
          <div className="messages">
            {messagePane}
          </div>
          <div className="input">
            <textarea onBlur={evt => this.setInput(evt)} />
            <button onClick={evt => this.sendInput(evt)}>Chat</button>
          </div>
        </div>
      );
    } else {
      ...
    }
  }

Los eventos generados por la entrada de mensajes se gestionan en setInput y en sendInput. setInput simplemente almacena el texto introducido en el estado del componente:

  setInput(evt) {
    this.setState({
      input: evt.target.value
    });
  }

El manejador del botón, sendInputtoma el texto almacenado en el estado y lo pasa a la Conversación usando sendText. A continuación, borra el texto en el estado y en el área de texto que lo precede:

 sendInput(evt) {
    this.state.conversation.sendText(this.state.input).then(() => {
      this.setState({
        input: null
      });
    });
    evt.target.previousSibling.value = '';
  }

¡Chatea!

Aunque le falta la gestión de errores y no presta atención al rendimiento, ahora tienes una aplicación de chat muy básica. Despojarse de las características de una aplicación de producción revela lo poco que necesita para proporcionar la funcionalidad básica de chat:

  1. Un usuario conectado a una aplicación Nexmo

  2. Una conversación para que el usuario se una

  3. Un manejador de eventos para los mensajes recibidos

  4. La función sendText para activar el chat

Ya sea que quieras crear una sala de chat de la vieja escuela, una conversación emergente para ayudar a clientes confundidos, o cualquier otra cosa, puedes construirla comenzando con estos elementos. No necesitas manejar ningún socket o polling. Y con React, no necesitas hacer nada para activar las actualizaciones del DOM. Ahora puedes centrar tu atención en la interfaz de usuario y en la robustez de la aplicación.

Compartir:

https://a.storyblok.com/f/270183/250x250/f231d97f1b/garann-means.png
Garann MeansDesarrollador Educador

Soy desarrollador de JavaScript y educador de desarrolladores en Vonage. A lo largo de los años me han entusiasmado las plantillas, Node.js, las aplicaciones web progresivas y las estrategias offline-first, pero lo que siempre me ha encantado es una API útil y bien documentada. Mi objetivo es hacer que tu experiencia usando nuestras APIs sea la mejor posible.