I'm facing an issue with my React and Socket.IO application. When a user joins a room, the user's information is getting added twice in the client's array on the client side. The entries have the same username but different socket IDs. Here's a simplified version of my code of server.js and react component
server.js
const express = require('express');
const app = express();
const http = require('http');
const path = require('path');
const { Server } = require('socket.io');
const ACTIONS = require('../src/constants/Actions');
const server = http.createServer(app);
const io = new Server(server);
const userSocketMap = {};
function getAllConnectedClients(roomId) {
return Array.from(io.sockets.adapter.rooms.get(roomId) || []).map(
(socketId) => {
return {
socketId,
username: userSocketMap[socketId],
};
}
);
}
io.on('connection', (socket) => {
console.log('socket connected', socket.id);
socket.on(ACTIONS.JOIN, ({ roomId, username }) => {
userSocketMap[socket.id] = username;
socket.join(roomId);
const clients = getAllConnectedClients(roomId);
clients.forEach(({ socketId }) => {
io.to(socketId).emit(ACTIONS.JOINED, {
clients,
username,
socketId: socket.id,
});
});
});
});
when I console.log the clients it shows console.log('client', clients); two users with the same name and different socket id.
import React, { useEffect, useState, useRef } from "react";
import Client from "../../components/Client";
import Ide from "../../components/Ide";
import { initSocket } from "../../socket";
import ACTIONS from "../../constants/Actions";
import { Navigate, useLocation, useNavigate, useParams } from "react-router";
import { toast } from "react-hot-toast";
import NewChat from "../../components/NewChat";
const EditorPage = () => {
const socketRef = useRef(null);
const codeRef = useRef(null);
const location = useLocation();
const { roomId } = useParams();
const [UsernName, setUsernName] = useState("");
const reactNavigator = useNavigate();
const [clients, setclients] = useState([]);
useEffect(() => {
const init = async () => {
socketRef.current = await initSocket();
socketRef.current.on("connect_error", (err) => handleErrors(err));
socketRef.current.on("connect_failed", (err) => handleErrors(err));
function handleErrors(e) {
console.log("socket error", e);
toast.error("Socket connection failed , try again later.");
reactNavigator("/");
}
socketRef.current.emit(ACTIONS.JOIN, {
roomId,
username: location.state?.username,
});
//listening for joined event
socketRef.current.on(
ACTIONS.JOINED,
({ clients, username, socketId }) => {
console.log(username);
console.log(location.state?.username);
console.log(socketId);
if (username !== location.state?.username) {
toast.success(`${username} joined the room`);
console.log(`${username} joined`);
}
if(username===location.state?.username){
setUsernName(username);
}
console.log(clients);
setclients(clients); // Update the clients state
socketRef.current.emit(ACTIONS.SYNC_CODE, {
code: codeRef.current,
socketId,
});
}
);
// listening for disconnecting
socketRef.current.on(ACTIONS.DISCONNECTED, ({ socketId, username }) => {
toast.success(`${username} left the room`);
setclients((prev) => {
return prev.filter((client) => client.socketId !== socketId);
});
});
};
init();
return () => {
//cleaning when component unmount
if (socketRef.current) {
socketRef.current.off(ACTIONS.JOINED);
socketRef.current.off(ACTIONS.DISCONNECTED);
socketRef.current.disconnect();
}
};
}, []);
if (!location.state) {
return <Navigate to="/" />;
}
return (
<>
<div className="mainWrap">
<div className="edtiroWrap">
<Ide
socketRef={socketRef}
roomId={roomId}
onCodeChange={(code) => {
codeRef.current = code;
}}
UsernName={UsernName}
/>
</div>
<div className="aside">
<div className="asideInner">
<h3>Connected</h3>
<div className="clientList">
{clients.map((client) => (
<Client key={client.socketId} username={client.username} />
))}
</div>
</div>
</div>
</div>
<NewChat socketRef={socketRef} clients={clients} roomId={roomId} />
</>
);
};
export default EditorPage;
I am also clearing the socket in return statement, still it is not working