Reputation: 38
Problem:
I am working on a chat application. When I send more than 9-10 requests, the browser slows down and eventually, it just hangs. On refreshing the page, everything is back to normal. I searched the socket.io documentation but couldn't get any solution regarding this matter.
Code:
Here is my Backend Express.JS code:
index.js
const express = require("express");
const { addUser, getUser, deleteUser, getAllUsers } = require("./users_api");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
const port = 5000 || process.env.PORT;
io.on("connection", (socket) => {
socket.on("join", ({ name, room }, callback) => {
const { err, user } = addUser({ id: socket.id, name, room });
if (err) return callback(err);
if (user) {
socket.emit("message", {
user: "admin",
text: `${user.name} has entered the room ${user.room}.`,
});
socket.broadcast.to(user.room).emit("message", {
user: "admin",
text: `${user.name} has joined the room.`,
});
socket.join(user.room);
io.to(user.room).emit("users", {
room: user.room,
users: getAllUsers(user.room),
});
callback();
}
});
socket.on("client_message", (msg) => {
const user = getUser(socket.id);
if (user) io.to(user.room).emit("message", { user: user.name, text: msg });
});
socket.on("disconnect", () => {
console.log(6);
const user = deleteUser(socket.id);
if (user) {
io.to(user.room).emit("message", {
user: "admin",
text: `${user.name} has left the room.`,
});
io.to(user.room).emit("users", {
room: user.room,
users: getAllUsers(user.room),
});
}
});
});
server.listen(port, () => console.log(`Server started at port ${port}.`));
users_api.js:
const current_users = [];
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase().split(" ").join("_");
room = room.trim().toLowerCase();
const exist_user = current_users.find(
(user) => name === user.name && room === user.room
);
if (exist_user)
return {
err: "User with this name already exists.",
};
current_users.push({ id, name, room });
return { user: { id, name, room } };
};
const deleteUser = (id) => {
const index = current_users.findIndex((user) => user.id === id);
if (index !== -1) return current_users.splice(index, 1)[0];
};
const getUser = (id) => current_users.find((user) => user.id === id);
const getAllUsers = (room) =>
current_users.filter((user) => user.room === room);
module.exports = { addUser, deleteUser, getUser, getAllUsers };
Front-end Code:
import io from "socket.io-client";
import React, { useEffect, useRef, useState } from "react";
import queryString from "query-string";
import "./ChatPage.css";
const END_POINT = "http://localhost:5000";
const ChatPage = (props) => {
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [users, setUsers] = useState([]);
const socket = useRef(null);
const handleSubmit = () => {
socket.current.emit("client_message", message);
setMessage("");
};
useEffect(() => {
const { name, room } = queryString.parse(props.location.search);
socket.current = io(END_POINT);
socket.current.emit("join", { name, room }, (error) => {
if (error) alert(error);
});
return () => {
socket.current.disconnect();
socket.current.off();
};
}, [props.location.search, END_POINT]);
useEffect(() => {
socket.current.on("message", (msg) => {
setMessages([...messages, msg]);
});
socket.current.on("users", ({ users }) => {
setUsers(users);
});
}, [messages.length]);
return (
<div className="chat-room-container">
<div className="chat-room-left">
<ul>
{messages.map((message, i) => (
<li key={i}>
{message.name}
{message.text}
</li>
))}
</ul>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button type="button" onClick={handleSubmit}>
Submit
</button>
</div>
<div className="chat-room-right">
<ul>
{users.map((user, i) => (
<li key={i}>{user.name}</li>
))}
</ul>
</div>
</div>
);
};
export default ChatPage;
I don't understand what's wrong, please help.
Upvotes: 2
Views: 414
Reputation: 36
Instead of sending a message on event or broadcast try to send list of messages to particular user or all users list with their messages belonging to same room. This will leads to less render in frontend.
For example, Your Frontend code will change like,
useEffect(() => {
socket.current.on("message", (msgList) => {
setMessages(msgList);
});
socket.current.on("users", ({ users }) => {
setUsers(users);
});
}, [messages.length]);
Also if different rooms then there occurs a problem to filter the messages. So to solve this there are two possible ways:
As this solution is not a good practice as every time the whole list of message will come and applying filter on that will increase time complexity. But you can reduce to some extent by making proper structure of data state on server.
I think this will help you.
Upvotes: 2
Reputation: 1197
You are creating new socket connection on every useEffect, so after ten messages, you have ten connections.
socket = io(END_POINT);
I store the created socket connection in useRef - so if it is created again, it is overwritten with the new one - and it does not duplicate.
const socketConection = useRef(null)
useEffect(() => {
socketConnection.current = io(END_POINT)
}, [deps])
Of course, in all following uses you have to use socketConection.current
Upvotes: 2