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

ReactとNexmoでチャットアプリを作る

最終更新日 May 10, 2021

所要時間:1 分

オンライン・コミュニケーションの最もシンプルな方法のひとつが、ウェブサイトに追加する最もシンプルな方法のひとつでもある。 リアクトNexmoのクライアントサイドJavaScriptツール.チャットアプリケーションは、顧客サービスを提供したり、プロジェクトでのコラボレーションを促進したり、友人とキャッチアップしたりすることができます。そして朗報です。 ReactとExpressのフルスタックチュートリアルをご覧になっているのであれば、チャット・アプリケーションを作るために必要なものはほとんど揃っています。

前提条件

物事を簡潔にするために、以下を仮定しよう。 チュートリアル ReactとExpressのチュートリアル.

フルスタックのサンプルコードと同様に サンプルコードは、Glitchを使うことで後者の要件を満たします。

作成したNexmoアプリケーションをフルスタックアプリに使用する場合は、1つまたは2つの会話IDを生成し、次のステップで使用するためにテキストファイルにコピーすることができます。このプロジェクト用に新しいアプリケーションを作成する場合は、次のようにします。 コマンドラインから会話を作成する.

NexmoAppにチャットルームを追加する

機能を分離しておくには、Reactアプリケーションに次の場所で新しいコンポーネントを追加します。 client/src/Chatroom.js.今のところ、ほとんど空にしておいても構わない:

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

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

export default Chatroom;

また、CSSファイルを追加し、任意の段階で好きなスタイルを設定することもできる。

を開くと NexmoApp.jsを開くと、いくつかの Conversationコンポーネントへの参照があります。この Chatroomコンポーネントも同様に使用されるので、これらの参照を Chatroom.それらは importrender関数の中にあるはずだ。

render内で Chatroomを渡す代わりに invitesを渡す代わりに chats:

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

に渡される他のプロパティは、ユーザー認証とログインの一部としてすでに存在している。 Chatroomに渡される他のプロパティは、ユーザー認証とログインの一部としてすでに存在しているが chatsは現在ステートの一部ではありません。この単純なアプリでは、上記で作成したConversationsをコンポーネントの初期状態にハードコードします。エンドユーザーと区別するために、好きな名前を付けることができます:

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

このアプリは会話や招待を管理しません。 loginのコードを削除することもできます。これにより、この機能はログインとNexmoアプリケーションへの参照を保存するだけになります:

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

シンプルなチャットルーム

すべての Userコンポーネント・コードだけを残しておくことができる。新しいユーザーを作成したり、既存のユーザーのリストを提供したりすることは同じです。一旦ユーザーがログインすると、チャットを続けることができます。

この Chatroomコンポーネントは2つの状態を含む: チャットルームの選択とチャットルーム自体。裏では、チャットルームは単なる Nexmo Conversationコンポーネントと同じように見えるでしょう。 Conversationコンポーネントのコードと似ています。両方の状態に必要な関数と条件式をスタブアウトして、始めることができます:

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;

チャットに参加する

ユーザーが選択する定義済みのチャットルームの有限セットを持っているので、ドロップダウンメニューを使用すると、簡単に選択することができます。チャットルームが1つしかない場合、このインターフェイスを完全に削除することができます。にハードコードされた2つのチャットルームのためのドロップダウンメニューを作成するには、配列をループして NexmoAppにハードコードされた2つのチャットルームのドロップダウンリストを作成するには、配列をループして optionの子として追加します。 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>
      );
    }
  }

ドロップダウンの値が変わると joinConversationがトリガーされます。ハンドラ joinConversationハンドラはNexmoアプリから選択された会話をIDで取得し、その会話に参加します。また、そのハンドラへの参照を保存し、メッセージを受信するための別のイベントハンドラを割り当てます:

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

この onMessageハンドラは、アクティブな会話に新しい textイベントが発生するたびにトリガーされます。イベントをトリガーしたConversation Memberとイベントオブジェクト自体の情報を受け取ります。シンプルなチャットでは、これらの情報のほとんどを破棄し、ID、ユーザー表示名、メッセージテキストのみを保存することができます。この情報は、ステートに保存されているメッセージのリストに連結することができます:

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

もしこのような単純なチャットを本番用に設計するのであれば、時間が経ったら古いメッセー ジを別のストレージオブジェクトに移動するように計画したいでしょう。かなりの量のトラフィックがある場合、すべてのメッセージを格納する単一のアレイは必然的に問題を引き起こします。

メッセージの送信

ユーザーがログインしてチャットに参加したら、メッセージを送受信したいと思うでしょう。これは、最低限、メッセージを見るためのエリアとテキストの入力フィールドを持つ UI をレンダリングしたいことを意味します。このための JSX は、メイン条件分岐の render.メッセージの配列を繰り返し、ユーザーがチャットに参加してから受信したものをレンダリングします。その下に、新しく入力されたテキストをセットし、送信するテキストエリアとボタンをそれぞれ提供します:

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

メッセージ入力によって発生したイベントは setInputsendInput. setInputで処理され、入力されたテキストをコンポーネントの状態に格納するだけです:

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

ボタンハンドラ sendInputステートメントに格納されたテキストを受け取り、それを sendText.そして、ステートとその前のテキストエリアのテキストをクリアします:

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

チャット・アウェイ

エラー処理もなく、パフォーマンスにも気を配っていませんが、これでとても基本的なチャット・アプリケーションが出来上がりました。本番アプリの機能を取り除くことで、コアとなるチャット機能を提供するために必要なものがいかに少ないかがわかります:

  1. Nexmoアプリケーションにログインしているユーザ

  2. ユーザーが参加できる会話

  3. 受信メッセージのイベントハンドラ

  4. チャット機能 sendTextチャット機能

昔ながらのチャットルーム、混乱した顧客を助けるためのポップアップ会話、または何か他のものを作成したい場合でも、これらの要素から始めることができます。ソケットやポーリングを処理する必要はない。Reactを使えば、DOMの更新をトリガーするために何かをする必要もない。これで、UIとアプリの堅牢性に注意を向けることができる。

シェア:

https://a.storyblok.com/f/270183/250x250/f231d97f1b/garann-means.png
Garann Meansデベロッパー・エデュケーター

私はJavaScript開発者で、Vonageの開発者教育者です。長年にわたり、テンプレート、Node.js、プログレッシブ・ウェブ・アプリケーション、そしてオフライン・ファースト戦略に熱中してきましたが、私がいつも本当に愛しているのは、便利できちんと文書化されたAPIです。私の目標は、当社のAPIを使用するお客様の体験を、私がお手伝いできる最高のものにすることです。