Introduction

L’objectif de ce chapitre est de vous permettre de créer une application “temps réel” :

  • un serveur Express indépendant qui reçoit les messages de chaque client et les renvoie à l’ensemble des utilisateurs connectés à l’aide de la bibliothèque socket.io.
  • une application React qui permet à chaque client d’écrire des messages, et de les envoyer au serveur, ce qui permet de communiquer avec tous les clients connectés

Avant de commencer

  • Créez un répertoire <APP> dans lequel allez écrire le code de votre application. <APP> correspond à un nom de votre choix.

Le client avec React.js

  1. Dans votre répertoire <APP>, créez une application React.js appelée client par la commande suivante :

    npx create-react-app client

    ou alors téléchargez l’archive suivante qui contient une application React vide.

  2. Ajoutez le package bootswatch à votre client npm i bootswatch pour pouvoir disposer du style bootstrap utilisé dans le code présenté par la suite.

  3. Supprimez le code du fichier client/src/App.js et remplacez-le progressivement en suivant les différentes étapes ci-dessous :

    function App() {
      const [messages, setMessages] = useState([]);
    • Le début de la fonction déclare une variable d’état messages qui est un tableau, vide à l’origine.
    • A chaque modification de ce tableau, la page est réaffichée, c’est à cela que servent les variables d’état.
  4. Ajoutez ensuite la fonction suivante :

    function setNewMessage(msg) {
        setMessages([
          ...messages,
          msg
        ]);
      }
    • Cette fonction permet de remplacer la variable d’état messages par un nouveau tableau qui contient un message en plus.
    • Tout appel à cette fonction déclenche le réafficahe de la page.
  5. Poursuivez en ajoutant la fonction suivante :

    function sendMessage(e) {
        e.preventDefault();
        const msg = {
          username: e.target.username.value,
          text: e.target.text.value
        };
        setNewMessage(msg);
      }
    • Cette fonction prend en argument un évènement, la validation d’un formulaire qui sera créé à l’étape suivante.
    • Le formulaire contient un champ username et un champ text qui contiennent l’identité d’une personne qui poste un message, ainsi que le texte de ce message.
    • L’appel à la fonction setNewMessage permet de réafficher la page à chaque nouveau message.
  6. Terminant en plaçant le code suivant qui clos la définition de la fonction :

      return (
          <div className="container">
            <div className="row">
              <div className="col-4">
                <div className="card">
                  <div className="card-body">
                    <div className="card-title">My first chat</div>
                    <hr/>
                    <div className="messages">
                      {messages.map(msg => {
                        return (
                            <div key>{msg.username}: {msg.text}</div>
                        )
                      })}
                    </div>
                  </div>
                  <form onSubmit={e => sendMessage(e)}>
                    <div className="card-footer">
                      <input id="username"
                             type="text"
                             placeholder="Username"
                             className="form-control"
                      />
                      <br/>
                      <input id="text"
                             type="text"
                             placeholder="Your message"
                             className="form-control"
                      />
                      <br/>
                      <button type="submit"
                              className="btn btn-primary form-control">
                        send
                      </button>
                    </div>
                  </form>
                </div>
              </div>
            </div>
          </div>
      );
    }
    
    export default App;
    • Ce code vous permet de faire apparaître un formulaire de saisie de message le client.
    • Ce formulaire est pour le moment inactif. Vous le ferez interagir avec le serveur dès que celui-ci sera créé.
    • Vous pouvez bien évidemment utiliser un autre style de bootstwatch si vous le souhaitez.
    • Testez votre application : à chaque validation de formulaire, le texte du message apparaît

Le serveur avec express

  1. Dans <APP>, créez un répertoire server.

  2. Dans le répertoire server saisissez la commande npm init -y pour créer votre serveur.

  3. Créez le fichier server/server.js

  4. Modifiez l’entrée “scripts” du fichier server/package.json comme suit :

    "scripts": {
        "start": "nodemon server.js"
      },
  5. Ajoutez les packages express et socket.io :

    npm i express socket.io
  6. Vous pouvez maintenant recopier le code suivant dans server/server.js :

    const express = require('express');
    const socket = require('socket.io');
    
    const app = express();
    const PORT = 8000;
    
    const server = app.listen(PORT, () => {
        console.log('server is running on port ' + PORT)
    });
    
    const io = socket(server);
    
    io.on('connection', socket => {
        console.log("socket=",socket.id);
    });
    • On crée ici un objet de la classe Socket qui interagit avec votre navigateur. (voir la documentation ici)
    • Lorsqu’un client établit une connexion, l’évènement connection est détecté et la fonction anonyme associée est déclenchée.
  7. Lancez maintenant le serveur :

    npm start

    A chaque fois qu’un client établit une connection à votre serveur, l’identifiant de connexion s’affiche au niveau du serveur. Pour le moment rien ne s’affiche car le client n’établit pas de connexion.

La connection client-serveur

  1. Sur le client, ajoutez le package socket.io-client :

    npm i socket.io-client
  2. Importez le package dans client/App.js :

    import io from 'socket.io-client';
  3. A la suite de la déclaration de la variable d’état messages, ajoutez la connexion au serveur :

    const socket = io('localhost:8000');
    • Cette fois vous y êtes, le client établit une connexion sur le port 8000.
    • Sur le serveur, vous voyez maintenant apparaître l’identifiant de connexion entre le client et le serveur.
  4. Ajoutez ensuite le code suivant :

    socket.on('SERVER_MSG', msg => {
        setNewMessage(msg);
    });
    • A chaque fois que l’object socket intercepte un évènement SERVER_MSG, il déclenche la fonction anonyme associée.
  5. Modifiez comme suit la fonction sendMessage :

    function sendMessage(e) {
        e.preventDefault();
        const msg = {
            username: e.target.username.value,
            text: e.target.text.value
        };
        socket.emit('CLIENT_MSG', msg);
        setNewMessage(msg);
    }
    • Vous avez juste ajouté la ligne socket.emit('CLIENT_MSG', msg); qui permet d’envoyer un message sur le port 8000 à l’aide de la méthode emit. Cette méthode génère un évènement CLIENT_MSG, qu’il revient au serveur d’intercepter.
    • Ouvrez un nouvel onglet sur votre navigateur, et relancez http://localhost:3000/.
    • Remarquez que les messages émis par l’un ou l’autre des deux clients restent locaux à ces clients.
  6. Modifiez le serveur comme suit :

    io.on('connection', socket => {
        console.log("socket=",socket.id);
        socket.on('CLIENT_MSG', data => {
            console.log("msg=",data);
            io.emit('SERVER_MSG', data);
        })
    });
    • Votre serveur intercepte maintenant les évènements CLIENT_MSG émis sur le port 8000 et renvoie les données reçues de cette manière en émettant un évènement SERVER_MSG qui renvoie les mêmes données.
    • Ainsi, tout client à l’écoute d’un évènement SERVER_MSG intercepte les messages.
    • Vous pouvez voir apparaître les messages sur le serveur.
    • Vous pouvez également constater que les deux clients s’échangent des messages.