https://a.storyblok.com/f/270183/214343/a5c0290130/react_nexmo.png

Crie um aplicativo de bate-papo com React e Nexmo

Publicado em May 10, 2021

Tempo de leitura: 4 minutos

Uma das formas mais simples de se comunicar online também está entre as mais fáceis de incorporar ao seu site, usando React e as ferramentas JavaScript do lado do cliente da Nexmo. Um aplicativo de chat pode oferecer atendimento ao cliente, facilitar a colaboração em um projeto ou permitir que você converse com amigos. E uma boa notícia: se você acompanhou nosso tutorial anterior tutorial completo sobre React e Express, você já tem a maior parte do que precisa para criar um.

Pré-requisitos

Para resumir, vamos supor que você tenha acompanhou o tutorial do React e do Express.

Assim como no código de exemplo do full-stack, o código de exemplo deste tutorial atenderá ao último requisito utilizando o Glitch.

Se você usar o aplicativo Nexmo que criou para seu aplicativo full-stack, poderá gerar um ou dois IDs de conversa e copiá-los para um arquivo de texto para uso na próxima etapa. Se preferir criar um novo aplicativo para este projeto, você pode criar as conversas a partir da linha de comando.

Adicionando salas de bate-papo ao NexmoApp

Para manter as funcionalidades separadas, adicione um novo componente ao seu aplicativo React em client/src/Chatroom.js. Por enquanto, você pode deixá-lo praticamente vazio:

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

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

export default Chatroom;

Você também pode adicionar o arquivo CSS dele e preenchê-lo com o estilo que desejar, a qualquer momento.

Se você abrir NexmoApp.js você verá algumas referências ao Conversation componente. O Chatroom componente será usado de maneira semelhante, então você pode simplesmente substituir essas referências por Chatroom. Elas devem estar no imports e na render função.

Dentro de render, altere a Chatroom tag do componente apenas um pouquinho, de modo que, em vez de passar invites, você passe chats:

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

As outras propriedades passadas para Chatroom já existem como parte da autenticação e do login do usuário, mas chats atualmente não fazem parte do estado. Para este aplicativo simples, insira manualmente as “Conversas” que você criou acima no estado inicial do componente. Você pode dar a elas os nomes que quiser para diferenciá-las para os usuários finais:

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);
  }

Como este aplicativo não gerencia conversas nem convites, você também pode excluir o código em login para acessar o Conversations. Isso faz com que essa função se limite a fazer o login e armazenar uma referência ao aplicativo Nexmo:

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

Uma sala de bate-papo simples

Você pode deixar todo o User código do componente como está. Ele continuará funcionando da mesma forma, criando um novo usuário ou exibindo uma lista dos usuários existentes. Assim que o usuário estiver conectado, ele poderá prosseguir para o chat.

O Chatroom componente terá dois estados: a escolha de uma sala de bate-papo e a própria sala de bate-papo. Nos bastidores, uma sala de bate-papo é apenas um Conversa da Nexmo; portanto, parte do código desse componente será semelhante ao do Conversation componente. Você pode criar esboços das funções e condicionais necessárias para ambos os estados para começar:

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;

Entrar em um chat

Como o usuário tem um conjunto finito de salas de bate-papo predefinidas para escolher, um menu suspenso permitirá que ele selecione uma delas com facilidade. Se houvesse apenas uma única sala de bate-papo, seria possível dispensar completamente essa interface. Para criar um menu suspenso para as duas salas de bate-papo codificadas diretamente em NexmoApp, percorra o array para criar um conjunto de options e, em seguida, adicione-os como filhos de um 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>
      );
    }
  }

Quando o valor do menu suspenso muda, joinConversation o evento é acionado. O joinConversation manipulador obterá a conversa escolhida pelo seu ID no aplicativo Nexmo e, em seguida, entrará nela. Ele também armazena uma referência a ela e atribui a ela outro manipulador de eventos para mensagens recebidas:

  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
      });
    });
  }

O onMessage manipulador é acionado sempre que há um novo text evento na Conversação ativa. Ele recebe informações sobre o Participante da Conversação que acionou o evento e o próprio objeto do evento. Para um bate-papo simples, você pode descartar a maior parte dessas informações e salvar apenas o ID, o nome de exibição do usuário e o texto da mensagem. Essas informações podem ser concatenadas a uma lista de mensagens armazenadas no 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
    });
  }

Se você estivesse projetando até mesmo um chat simples como este para uso em produção, seria recomendável planejar a transferência das mensagens mais antigas para um objeto de armazenamento diferente após algum tempo. Com qualquer volume significativo de tráfego, um único array para armazenar todas as mensagens inevitavelmente causará problemas.

Envio de mensagens

Assim que o usuário estiver conectado e tiver entrado em um chat, ele vai querer enviar e receber mensagens. Isso significa que você deve exibir uma interface de usuário com, no mínimo, uma área para visualizar mensagens e um campo de entrada de texto. O JSX para isso preenche o outro ramo da condicional principal em render. Ele percorre seu array de mensagens e renderiza tudo o que foi recebido desde que o usuário entrou no chat. Abaixo disso, ele fornece uma área de texto e um botão que, respectivamente, definem o texto recém-digitado e o enviam:

  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 {
      ...
    }
  }

Os eventos gerados pela entrada da mensagem são tratados em setInput e sendInput. setInput simplesmente armazena o texto inserido no estado do componente:

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

O manipulador do botão, sendInput, pega o texto armazenado no estado e o passa para a Conversação usando sendText. Em seguida, ele limpa o texto no estado e na área de texto que o precede:

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

Converse à vontade!

Embora não tenha tratamento de erros e não leve em conta o desempenho, agora você tem um aplicativo de bate-papo bem básico. Ao eliminar os recursos de um aplicativo de produção, fica claro o quão pouco é necessário para oferecer a funcionalidade essencial de um bate-papo:

  1. Um usuário que fez login em um aplicativo da Nexmo

  2. Uma conversa na qual o usuário pode participar

  3. Um manipulador de eventos para mensagens recebidas

  4. A sendText função para ativar o bate-papo

Se você quer criar uma sala de bate-papo à moda antiga, uma janela de conversa pop-up para ajudar clientes confusos ou qualquer outra coisa, pode desenvolvê-la a partir desses elementos. Você não precisa lidar com sockets nem com polling. E, com o React, não é preciso fazer nada para acionar atualizações no DOM. Agora você pode concentrar sua atenção na interface do usuário e na robustez do aplicativo.

Compartilhar:

https://a.storyblok.com/f/270183/250x250/f231d97f1b/garann-means.png
Garann MeansFormador de Desenvolvedores

Sou desenvolvedor de JavaScript e instrutor de desenvolvimento na Vonage. Ao longo dos anos, tenho me interessado muito por modelos, Node.js, aplicativos web progressivos e estratégias “offline-first”, mas o que sempre adorei de verdade é uma API útil e bem documentada. Meu objetivo é tornar a sua experiência com nossas APIs a melhor possível.