¿Cómo administrar usuarios en socket.io en Node.js?

Socket.IO es una biblioteca que permite la comunicación en tiempo real, bidireccional y basada en eventos entre el navegador y el servidor.

Enfoque: primero, es importante tener en cuenta que cuando se crea un nuevo socket, se le asigna una identificación única que se recupera llamando a socket.id. Esta identificación se puede almacenar dentro de un objeto de usuario y podemos asignar un identificador como un nombre de usuario. Para el frontend, usaremos React, y para el backend node.js y express. Cree dos carpetas en su servidor de nombres de directorio principal (backend) y cliente (frontend). Socket.on será un evento que se llamará cuando sea necesario, y el evento es llamado por socket.emit, donde llamamos al evento y pasamos el parámetro si es necesario.

Primero crearemos una parte de backend de la aplicación:

Paso 1: instalar dependencias para backend

npm init
npm install cors
npm install express
npm install nodemon
npm install socket.io
npm install http

Estructura del proyecto: Tendrá el siguiente aspecto.

Paso 2: Crear Index.js. Socket.on se usa para unirse cada vez que un usuario se unirá, el frontend emitirá un evento de unión y el backend emitirá un evento de mensaje y enviará el mensaje de que el usuario se ha unido.

Hay tres métodos utilizados dentro de index.js:

  • conexión: cada vez que un usuario se unirá a la sala desde la página de inicio, se activará el evento de conexión y todos en la sala recibirán el mensaje de que el usuario se ha unido, y el usuario se agregará a los datos de esa sala.
  • sendMessage: ahora, cuando cualquiera de los usuarios envíe el mensaje desde la entrada en el chat, se activará el evento sendmessage y el mensaje se agregará dentro de la array de mensajes con el nombre de usuario.
  • desconectar: ​​cuando el usuario abandona el chat, se activará el evento de desconexión y el usuario se eliminará de la array de usuarios de esa sala y se enviará el mensaje de que el usuario abandonó el chat.

Nombre de archivo: index.js

Javascript

const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const cors = require('cors');
const { addUser, removeUser, getUser,
        getUsersInRoom } = require("./users");
 
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors())
 
io.on("connection", (socket) => {
    socket.on('join', ({ name, room }, callback) => {
 
        const { error, user } = addUser(
            { id: socket.id, name, room });
 
        if (error) return callback(error);
 
        // Emit will send message to the user
        // who had joined
        socket.emit('message', { user: 'admin', text:
            `${user.name},
            welcome to room ${user.room}.` });
 
        // Broadcast will send message to everyone
        // in the room except the joined user
        socket.broadcast.to(user.room)
            .emit('message', { user: "admin",
            text: `${user.name}, has joined` });
 
        socket.join(user.room);
 
        io.to(user.room).emit('roomData', {
            room: user.room,
            users: getUsersInRoom(user.room)
        });
        callback();
    })
 
    socket.on('sendMessage', (message, callback) => {
 
        const user = getUser(socket.id);
        io.to(user.room).emit('message',
            { user: user.name, text: message });
 
        io.to(user.room).emit('roomData', {
            room: user.room,
            users: getUsersInRoom(user.room)
        });
        callback();
    })
 
    socket.on('disconnect', () => {
        const user = removeUser(socket.id);
        if (user) {
            io.to(user.room).emit('message',
            { user: 'admin', text:
            `${user.name} had left` });
        }
    })
 
})
 
server.listen(process.env.PORT || 5000,
    () => console.log(`Server has started.`));

Paso 3: Cree un archivo User.js que contenga las siguientes funciones.

Estas son las funciones que almacenan usuarios, obtienen usuarios, eliminan usuarios y se usan en el archivo index.js.

  • addUser: esta función se llamará cada vez que se realice una conexión o se una un nuevo usuario y el nombre de usuario se almacene en la array de esa sala y si el nombre de usuario ya está en uso por cualquiera de los usuarios dentro de esa sala, el usuario no tendrá acceso para charlar.
  • removeUser: esta función se llamará cada vez que se destruya una conexión o un usuario se vaya y el nombre de usuario se elimine de la array de esa sala.
  • getUser: esta función toma id como parámetro y devuelve el nombre de usuario buscándolo en la array.
  • getUsersInRoom: esta función devolverá el nombre de todos los usuarios en la sala para mostrar en el chat.

nombre de archivo: Usuario.js

Javascript

const users = [];
 
const addUser = ({id, name, room}) => {
    name = name.trim().toLowerCase();
    room = room.trim().toLowerCase();
 
    const existingUser = users.find((user) => {
        user.room === room && user.name === name
    });
 
    if(existingUser) {
        return{error: "Username is taken"};
    }
    const user = {id,name,room};
 
    users.push(user);
    return {user};
 
}
 
const removeUser = (id) => {
    const index = users.findIndex((user) => {
        user.id === id
    });
 
    if(index !== -1) {
        return users.splice(index,1)[0];
    }
}
 
const getUser = (id) => users
        .find((user) => user.id === id);
 
const getUsersInRoom = (room) => users
        .filter((user) => user.room === room);
 
module.exports = {addUser, removeUser,
        getUser, getUsersInRoom};

Ahora trabajaremos en la parte frontal de la aplicación:

Paso 1: Instalar reaccionar para frontend.

npx create react-app "client" 

Paso 2: después de instalar React, instale las dependencias para Project dentro de la carpeta del cliente.

  npm install query-string;
  npm install react-emoji;
  npm install react-router;
  npm install socket.io-client;

Estructura del proyecto: Tendrá el siguiente aspecto.

Paso 3: dentro de App.js, cree rutas para las páginas unirse a la página y la página de chat e importe los componentes para que ambas páginas se muestren en esa ruta.

Nombre de archivo: App.js

Javascript

import React from 'react';
 
import Chat from './components/Chat/Chat';
import Join from './components/Join/Join';
 
import { BrowserRouter as Router, Route }
        from "react-router-dom";
 
const App = () => {
  return (
    <Router>
      <Route path="/" exact component={Join} />
      <Route path="/chat" component={Chat} />
    </Router>
  );
}
 
export default App;

Paso 4: Dentro de Chat.jssocket.emit está llamando a la unión y otros eventos, y socket.on está creando eventos llamados desde el backend como para almacenar mensajes, datos de usuarios. Este es el componente principal y llamará a todos los demás componentes mediante el envío de parámetros como nombres de usuario, mensajes para mostrar en el chat. Cada vez que los usuarios accedan a esta página, se llamará al evento de unión desde el backend. Cada vez que cambien los mensajes o los datos del usuario, es decir, si un usuario se une o se va, o si un usuario publica, se llamará a un evento de mensaje y se llamará al evento de datos de habitación dentro del efecto de uso para mostrar el mensaje de entrada o salida del usuario y almacenarlo en la array de mensajes. La barra de información mostrará el nombre de la habitación. El contenedor de texto mostrará el nombre de todos los usuarios en la sala. Mensajes mostrará todos los mensajes en el chat. Input tomará la entrada, la almacenará en una array y activará el evento sendmessage para enviar el mensaje con el nombre de usuario.

Nombre de archivo: Chat.js

Javascript

import React, {useState, useEffect} from "react";
import queryString from "query-string";
import io from 'socket.io-client';
import TextContainer from '../TextContainer/TextContainer';
import Messages from '../Messages/Messages';
import InfoBar from '../InfoBar/InfoBar';
import Input from '../Input/Input';
 
 import "./Chat.css";
 
var connectionOptions =  {
    "force new connection" : true,
    "reconnectionAttempts": "Infinity",
    "timeout" : 10000,                 
    "transports" : ["websocket"]
};
 
var socket = io.connect('https://localhost:5000',connectionOptions);
 
 
const Chat = ({location}) => {
 
    const [name, setName] = useState('');
    const [room, setRoom] = useState("");
    const [users, setUsers] = useState('');
    const [message, setMessage] = useState('');
    const [messages, setMessages] = useState([]);
 
    const ENDPOINT = 'localhost:5000';
 
    useEffect( () => {
        const {name, room} = queryString.parse(location.search);
 
        setName(name);
        setRoom(room);
 
        socket.emit('join',{name, room}, (error) => {
            if(error) {
                alert(error);
            }
        })
        return () => {
            socket.emit('disconnect');
            socket.off();
        }
         
    },[ENDPOINT, location.search]);
 
    useEffect( () =>  {
        socket.on('message', (message) => {
            setMessages([...messages,message]);
        })
 
        socket.on("roomData", ({ users }) => {
            setUsers(users);
        });
    },[messages, users])
 
    //Function for Sending Message
    const sendMessage = (e) => {
        e.preventDefault();
        if(message) {
            socket.emit('sendMessage', message, () => setMessage(''))
        }
    }
 
    console.log(message ,messages);
 
    return (
        <div className="outerContainer">
            <div className="container">
          
                <InfoBar room={room} />
                <Messages messages={messages} name={name} />
                <Input message={message} setMessage={setMessage}
                 sendMessage={sendMessage} />
            </div>
            <TextContainer users={users}/>
        </div>
    )
};
 
export default Chat;

 
Paso 5: Cree Chat.css para agregar estilo a Chat.js.

Nombre de archivo: Chat.css

CSS

.outerContainer {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #1A1A1D;
  }
   
  .container {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    background: #FFFFFF;
    border-radius: 8px;
    height: 60%;
    width: 35%;
  }
   
  @media (min-width: 320px) and (max-width: 480px) {
    .outerContainer {
      height: 100%;
    }
   
    .container {
      width: 100%;
      height: 100%;
    }
  }
   
  @media (min-width: 480px) and (max-width: 1200px) {
    .container {
      width: 60%;
    }
  }

 
Paso 6: mostrar el nombre de la sala en el encabezado de la página y obtener el nombre de la sala del componente de chat.

Nombre de archivo: Infobar.js

Javascript

import React from 'react';
 
import './InfoBar.css';
 
const InfoBar = ({ room }) => (
  <div className="infoBar">
    <div className="leftInnerContainer">
      <h3>{room}</h3>
    </div>
    <div className="rightInnerContainer">
      <a href="/">Leave</a>
    </div>
  </div>
);
 
export default InfoBar;

 
Paso 7: Cree el archivo Infobar.css.

Nombre de archivo: Infobar.css

CSS

.infoBar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #2979FF;
    border-radius: 4px 4px 0 0;
    height: 60px;
    width: 100%;
  }
   
  .leftInnerContainer {
    flex: 0.5;
    display: flex;
    align-items: center;
    margin-left: 5%;
    color: white;
  }
   
  .rightInnerContainer {
    display: flex;
    flex: 0.5;
    justify-content: flex-end;
    margin-right: 5%;
  }
   
  .onlineIcon {
    margin-right: 5%;
  }

 
Paso 8: el usuario dará la entrada y enviará el mensaje, luego se llamará al evento sendmessage desde el backend y los almacenes de datos en la array de mensajes.

Nombre de archivo: Input.js

Javascript

import React from 'react';
 
import './Input.css';
 
const Input = ({ setMessage, sendMessage, message }) => (
  <form className="form">
    <input
      className="input"
      type="text"
      placeholder="Type a message..."
      value={message}
      onChange={({ target: { value } }) => setMessage(value)}
      onKeyPress={event => event.key === 'Enter'
              ? sendMessage(event) : null}
    />
    <button className="sendButton"
        onClick={e => sendMessage(e)}>Send</button>
  </form>
)
 
export default Input;

 
Paso 9: Cree el archivo Input.css.

Nombre de archivo: Input.css

CSS

.form {
    display: flex;
    border-top: 2px solid #D3D3D3;
  }
   
  .input {
    border: none;
    border-radius: 0;
    padding: 5%;
    width: 80%;
    font-size: 1.2em;
  }
   
  input:focus, textarea:focus, select:focus{
    outline: none;
  }
   
  .sendButton {
    color: #fff !important;
    text-transform: uppercase;
    text-decoration: none;
    background: #2979FF;
    padding: 20px;
    display: inline-block;
    border: none;
    width: 20%;
  }

 
Paso 10: Tomando la entrada de los usuarios, su nombre y la sala para unirse desde la página de inicio y cuando el usuario se une para enviar al usuario a la página de chat.

Nombre de archivo: Join.js

Javascript

import React, {useState} from "react";
import {Link} from 'react-router-dom';
  
import './Join.css';
  
const Join = () => {
  
    const [name, setName] = useState('');
    const [room, setRoom] = useState("");
  
    return (
        <div className="joinOuterContainer">
            <div className="joinInnerContainer">
                <h1 className="heading">Join</h1>
                <div>
                    <input placeholder="Name"
                        className="joinInput"
                        type="text"
                        onChange=
        {(event) => setName(event.target.value)} />
                </div>
 
                <div>
                    <input placeholder="Room"
                        className="joinInput mt-20"
                        type="text" onChange=
        {(event) => setRoom(event.target.value)} />
                </div>
 
                <Link onClick={e => (!name || !room) ?
                    e.preventDefault() : null}
                    to={`/chat?name=${name}&room=${room}`
                               }>
                    <button className={'button mt-20'}
                        type="submit">Sign In
                    </button>
                </Link>
            </div>
        </div>
    );
};
  
export default Join;

Paso 11: Cree un archivo Join.css para agregar estilo al componente Join.js.

Nombre de archivo: Join.css

CSS

html, body {
    font-family: 'Roboto', sans-serif;
    padding: 0;
    margin: 0;
}
   
#root {
    height: 100vh;
}
   
* {
    box-sizing: border-box;
}
   
.joinOuterContainer {
    display: flex;
    justify-content: center;
    text-align: center;
    height: 100vh;
    align-items: center;
    background-color: #1A1A1D;
}
   
.joinInnerContainer {
    width: 20%;
}
   
.joinInput {
    border-radius: 0;
    padding: 15px 20px;
    width: 100%;
}
   
.heading {
    color: white;
    font-size: 2.5em;
    padding-bottom: 10px;
    border-bottom: 2px solid white;
}
   
.button {
    color: #fff !important;
    text-transform: uppercase;
    text-decoration: none;
    background: #2979FF;
    padding: 20px;
    border-radius: 5px;
    display: inline-block;
    border: none;
    width: 100%;
}
   
.mt-20 {
    margin-top: 20px;
}
   
@media (min-width: 320px) and (max-width: 480px) {
    .joinOuterContainer {
        height: 100%;
    }
   
    .joinInnerContainer {
        width: 90%;
    }
}
   
button:focus {
    outline: 0;
}

 
Paso 12: mostrar los mensajes en el cuadro de chat y si los mensajes son del usuario actual, estarán en el lado derecho con un color de fondo diferente; de ​​lo contrario, estarán en el lado izquierdo con un color de fondo diferente.

Nombre de archivo: Mensaje.js

Javascript

import React from 'react';
 
import './Message.css';
 
import ReactEmoji from 'react-emoji';
 
const Message = ({ message: { text, user }, name }) => {
  let isSentByCurrentUser = false;
 
  const trimmedName = name.trim().toLowerCase();
 
  if(user === trimmedName) {
    isSentByCurrentUser = true;
  }
 
  return (
    isSentByCurrentUser
      ? (
        <div className="messageContainer justifyEnd">
          <p className="sentText pr-10">{trimmedName}</p>
 
 
          <div className="messageBox backgroundBlue">
            <p className="messageText colorWhite">
              {ReactEmoji.emojify(text)}
            </p>
 
 
          </div>
        </div>
        )
        : (
          <div className="messageContainer justifyStart">
            <div className="messageBox backgroundLight">
              <p className="messageText colorDark">
                {ReactEmoji.emojify(text)}
              </p>
 
 
            </div>
            <p className="sentText pl-10 ">{user}</p>
 
 
          </div>
        )
  );
}
 
export default Message;

 
Paso 13: Mensaje.css

CSS

.messageBox {
    background: #F3F3F3;
    border-radius: 20px;
    padding: 5px 20px;
    color: white;
    display: inline-block;
    max-width: 80%;
  }
   
  .messageText {
    width: 100%;
    letter-spacing: 0;
    float: left;
    font-size: 1.1em;
    word-wrap: break-word;
  }
   
  .messageText img {
    vertical-align: middle;
  }
   
  .messageContainer {
    display: flex;
    justify-content: flex-end;
    padding: 0 5%;
    margin-top: 3px;
  }
   
  .sentText {
    display: flex;
    align-items: center;
    font-family: Helvetica;
    color: #828282;
    letter-spacing: 0.3px;
  }
   
  .pl-10 {
    padding-left: 10px;
  }
   
  .pr-10 {
    padding-right: 10px;
  }
   
  .justifyStart {
    justify-content: flex-start;
  }
   
  .justifyEnd {
    justify-content: flex-end;
  }
   
  .colorWhite {
    color: white;
  }
   
  .colorDark {
    color: #353535;
  }
   
  .backgroundBlue {
    background: #2979FF;
  }
   
  .backgroundLight {
    background: #F3F3F3;
  }

 
Paso 14: enviar el mensaje uno por uno desde la array de mensajes y el nombre en el componente del mensaje que luego se muestra a los usuarios.

Nombre de archivo: Mensajes.js

Javascript

import React from 'react';
 
import Message from './Message/Message';
 
import './Messages.css';
 
const Messages = ({ messages, name }) => (
    <div>
    {messages.map((message, i) => <div key={i}>
       <Message message={message} name={name}/>
    </div>)}
 
  </div>
);
 
export default Messages;

 
Mensajes.css

CSS

.messages {
    padding: 5% 0;
    overflow: auto;
    flex: auto;
  }

 
Paso 15: Obtenemos la array de todos los usuarios en la sala y la mostramos en la página aplicando algo de css.

Nombre de archivo: TextContainer.js

Javascript

import React from 'react';
 
import onlineIcon from '../../icons/onlineIcon.png';
 
import './TextContainer.css';
 
const TextContainer = ({ users }) => (
  <div className="textContainer">
    {
      users
        ? (
          <div>
            <h1>People currently chatting:</h1>
            <div className="activeContainer">
              <h2>
                {users.map(({name}) => (
                  <div key={name} className="activeItem">
                    {name}
                    <img alt="Online Icon" src={onlineIcon}/>
                  </div>
                ))}
              </h2>
            </div>
          </div>
        )
        : null
    }
  </div>
);
 
export default TextContainer;

Nombre de archivo: TextContainer.css

CSS

.textContainer {
    display: flex;
    flex-direction: column;
    margin-left: 100px;
    color: rgb(201, 25, 25);
    height: 60%;
    justify-content: space-between;
  }
   
  .activeContainer {
    display: flex;
    align-items: center;
    margin-bottom: 50%;
  }
   
  .activeItem {
    display: flex;
    align-items: center;
  }
   
  .activeContainer img {
    padding-left: 10px;
  }
   
  .textContainer h1 {
    margin-bottom: 0px;
  }
   
  @media (min-width: 320px) and (max-width: 1200px) {
    .textContainer {
      display: none;
    }
  }

 
Paso para ejecutar el backend: vaya a la carpeta del backend y abra la terminal para escribir el siguiente comando.

npm start

Paso para ejecutar frontend: Entra en la carpeta y abre la terminal para escribir el siguiente comando.

npm start

Salida: abra el navegador y escriba localhost 3000 para ver la aplicación en ejecución.

Publicación traducida automáticamente

Artículo escrito por aryanagrawalfirozabad y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *