Socket.io-client on recieving event running useEffect two times - javascript

I'm trying to build a chat application,
The issue is whenever I send a emit an message to socket.io at the same time I recieve the same message two times. I got same message console.log two times.
Now this is my Server.js (Using express & Socket.io)
const express = require("express");
const app = express();
const http = require("http");
const cors = require("cors");
const { Server } = require("socket.io");
app.use(cors());
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
console.log(`User Connected: ${socket.id}`);
socket.on("join_room", (data) => {
socket.join(data);
console.log(`User with ID: ${socket.id} joined room: ${data}`);
});
socket.on("send_message", (data) => {
socket.to(data.room).emit("receive_message", data);
});
socket.on("disconnect", () => {
console.log("User Disconnected", socket.id);
});
});
server.listen(3001, () => {
console.log("SERVER RUNNING");
});
And this is my client's chat.js where I am sending prop of
const socket = io.connect("http://localhost:3001");
below is component
In this Chat component.
import React, { useEffect, useState } from "react";
function Chat({ socket, username, room }) {
useEffect(() => {
socket.on("receive_message", (data) => {
console.log(data);
});
return () => {
socket.off("receive_message", (data) =>
console.log(`receive_message off ${data}`)
);
};
}, [socket]);
return (
<>
<input
type="text"
placeholder="John..."
onChange={(event) => {
console.log(event.target.value);
}}
onKeyPress={(event) => {
if (event.key === "Enter") {
socket.emit("send_message", {
room: room,
author: username,
message: event.target.value,
});
}
}}
/>
</>
);
}
export default Chat;
After opening second tab I see two console.logs popping up with the same message. Can anybody tell me what I am doing wrong here or missing here?
I have tried removing React.StrictMode from index.js, it resolves the issue but I don't want to remove it. Also after shifting my react version from 18 to 17 it also resolves the issue, how I can tackle this issue in 18. Also I want to cover that issue in same chat.js component. Any help would be appreciated.
Thanks.

The problem is here:
useEffect(() => {
socket.on("receive_message", (data) => {
console.log(data);
});
return () => {
socket.off("receive_message", (data) =>
console.log(`receive_message off ${data}`)
);
};
}, [socket]);
You are properly trying to remove a listener on the return, but the problem is that you are removing the wrong listener--you have to pass the exact same function that you want to remove (because you can have two listeners on an event). This means that when the second render occurs during safe mode, you have two listeners added to that event.
Try this:
useEffect(() => {
const receiveMessage = (data) => {
console.log(data)
}
socket.on("receive_message", receiveMessage);
return () => {
socket.off("receive_message", receiveMessage);
};
}, [socket]);
That will make sure that the right function is removed. If this works, please make sure to mark my answer!

Related

useEffect hook does not work first load and refresh

I am trying to develop a web game for school using react, next.js, express, and socket io. Right now I get the user room id and name from frontend. I want to display the usernames of the people in a lobby. I use useffect function to fetch user list from server. The issue is user list only displays on a client just when a new user joins. I does not render show users on the lobby page at the first render and the first refresh. What am I doing wrong?
import {useEffect, useState} from "react";
import io from "socket.io-client";
function Playerz(){
// const socket = io("http://localhost:8000", {transports : ['websocket']})
const [socket] = useState(() => io('http://localhost:8000'));
const [playersL, setPlayersL] = useState([
{username:"",
roomid:""}]
);
useEffect(() =>{
socket.on("receive_list", (data) => {
//let uzers = data.replace(/[\])}["{(]/g, '');
//let ucerlist = uzers.split(",");
/* let key;
for(key in data) {
if (data.hasOwnProperty(key)) {
console.log( data[key] + "\n");
}
}*/
setPlayersL(Object.values(data));
if (playersL === null) {
return null
}
console.log('bp',playersL)
})
},[playersL]);
return(
<div style={{textAlign:"center",borderBottom:"2px solids black"}}>
{
playersL ?
(playersL.map((elem) =>(
<div key={elem.username}>{elem.username}</div>))) : (<div>No posts</div>)
}
</div>
)
}
export default function Refine(){
const Xapsul = <Playerz/>;
return Xapsul;
}```
server.js
const app = require('express')();
const server = require('http').createServer(app);
const io = require('socket.io')(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"]
}
});
let users = []
let numofusers = 0
io.on('connection', (socket) =>{
console.log(" Connected! ");
socket.on('joinroom',(data)=>{
// console.log('joinroom! '+ data.name);
socket.join(data.room);
users.push({username: data.author,
roomid:data.room});
// console.log(`Users with ID: ${data.author} joined room: ${data.room}`);
console.log(`Users with ID: ${(JSON.stringify(users))}`);
io.emit("receive_list", (users));
});
socket.on("disconnect",()=>{
console.log("User Disconnected",socket.id);
})
app.get('/api', function(req, res){
res.json(users); //also tried to do it through .send, but there data only on window in browser
});
})
server.listen(8000)

React does not update before another socket.io message

I have a node.js socket.io server that emits two messages with a 1000 ms delay. The messages are logged fine in the react app, however if I remove the 1000 ms delay, react does not update the first message's state before the second one is received.
The only way I was able to fix this issue was by using a global variable to store the first message. Is there a solution without a global variable?
Server code:
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
cors: {
origin: "*",
},
});
io.on("connection", (socket) => {
socket.emit("data1", "First data");
setTimeout(() => {
socket.emit("data2", "Second data");
}, 1000);
});
server.listen(3001, () => {
console.log("listening on *:3001");
});
Working react app using global variable:
var data1;
export default function Main() {
const socket = useContext(SocketContext);
const onData1 = useCallback((data) => {
console.log("Received data1:", data);
data1 = data;
}, []);
const onData2 = useCallback(
(data) => {
console.log("Received data2:", data);
console.log("Data1 is:", data1);
},
[data1]
);
useEffect(() => {
console.log("Binding socket listeners...");
socket.on("data1", onData1);
socket.on("data2", onData2);
return () => {
console.log("Unbinding socket listeners...");
socket.off("data1", onData1);
socket.off("data2", onData2);
};
}, [socket, onData1, onData2]);
return <></>;
}
Not working react app:
export default function Main() {
const socket = useContext(SocketContext);
const [data1, setData1] = useState("");
const onData1 = useCallback((data) => {
console.log("Received data1:", data);
setData1(data);
}, []);
const onData2 = useCallback(
(data) => {
console.log("Received data2:", data);
console.log("Data1 is:", data1);
},
[data1]
);
useEffect(() => {
console.log("Binding socket listeners...");
socket.on("data1", onData1);
socket.on("data2", onData2);
return () => {
console.log("Unbinding socket listeners...");
socket.off("data1", onData1);
socket.off("data2", onData2);
};
}, [socket, onData1, onData2]);
return <></>;
}

Socket.on is not firing

I am building a message board as a learning exercise and have working notifications and a chat app working with socket.io.
I am trying to integrate a basic video call functionality, but I am stumped on some basic preparations for that feature. My socket.on code isn't firing when the server emits the relevant function and I have no clue why. The console logs around the emits are all executing, so I know the code is being reached.
On the client my socket.on code is in the same component as my notifications and they are working and it is definitely mounted.
It's the userOff and receiveCall functions that aren't being executed (for whatever reason)...
Any help will be appreciated.
Server:
io.on('connection', (socket) => {
socket.emit('messageFromServer');
socket.on('messageToServer', (dataFromClient) => {
connectedUsers[dataFromClient.username] = socket;
});
socket.on('join', ({ username, room }) => {
socket.join(room);
socket.emit('message', 'Welcome!');
socket.broadcast
.to(room)
.emit('message', `${username} has joined the room!`);
});
socket.on('messageRoom', ({ username, room, message }) => {
socket.broadcast.to(room).emit('message', `${username}: ${message}`);
});
socket.on('call', ({ username, id }) => {
if (connectedUsers[username]) {
connectedUsers[username].emit('recieveCall', id);
console.log('online emitted');
} else {
socket.emit('userOff');
console.log('offline emitted');
}
});
socket.on('disconnect', () => {
socket.disconnect(true);
});
});
client:
import React, { useContext, useEffect } from 'react';
import socketIOClient from 'socket.io-client';
import StateContext from '../StateContext';
import DispatchContext from '../DispatchContext';
const endpoint = 'http://localhost:5000';
const Socket = () => {
const appState = useContext(StateContext);
const appDispatch = useContext(DispatchContext);
const socket = socketIOClient(endpoint);
useEffect(() => {
socket.on('messageFromServer', () => {
socket.emit('messageToServer', { username: appState.username });
});
socket.on('userOff', () => {
console.log('user offline');
});
socket.on('recieveCall', (id) => {
console.log('recieve call');
});
socket.on('mailNotification', () => {
document.getElementById('notifyMail').classList.add('notify');
});
socket.on('boardsNotification', () => {
document.getElementById('notifyBoards').classList.add('notify');
});
}, []);
return null;
};
export default Socket;
did you try call socket.open first
https://socket.io/docs/client-api/#socketopen
and listen the connect event
https://socket.io/docs/client-api/#Event-%E2%80%98connect%E2%80%99

send socket request from one component to different component

There is a two different component. One is patient and another is Doctor. The doctor can be on any page. When patient fires join event from his page to a particular doctor(e.g abc), the node server will then catch that event and will send JoinAccept event to 'abc doctor'. Following doctor should get notification that following patient wants to connect with you.
To achieve this, I could able to send 'Join' event up to server but could not able to listen JoinAccept event on doctor's end.
This is what I have done
context.js
import io from "socket.io-client";
import { SOCKET_URL } from "constants/url";
const SocketContext = React.createContext();
const SocketProvider = ({ children }) => {
const [socketClient, setSocketClient] = React.useState();
const [socketUpdated, setSocketUpdated] = React.useState(false);
React.useEffect(() => {
const socket = io(SOCKET_URL);
setSocketClient(socket);
return () => {
io.disconnect();
};
}, []);
React.useEffect(() => {
console.log("socketClient", socketClient);
if (socketClient) {
const tokenData =
!!localStorage.token &&
JSON.parse(atob(localStorage.token.split(".")[1]));
if (tokenData.user) {
console.log("user", tokenData.user);
socketClient.emit("clientData", tokenData.user);
socketClient.on("connected", msg => {
console.log("connected");
setSocketUpdated(true);
});
// setSocketUpdated(true);
}
}
}, [socketClient]);
console.log("socket updated", socketUpdated);
return (
<>
<SocketContext.Provider value={{ socket: socketClient, socketUpdated }}>
{children}
</SocketContext.Provider>
</>
);
};
export { SocketContext, SocketProvider };
Patient.js (it will fire Join event)
const Patient = () => {
const { socket } = React.useContext(SocketContext);
React.useEffect(() => {
const data = {
to: "abcdoctor#gmail.com",
from: "patient#gmail.com",
message: "Join a call"
};
socket.emit("Join", data);
}, [socket]);
return (
<div>
<h1>Patient</h1>
</div>
);
};
Doctor.js
const DoctorParentComponent = () => {
return (
<>
<SocketProvider>
<h1>Navbar</h1>
<DoctorRoutes />
</SocketProvider>
</>
);
};
DoctorPage.js
const DoctorPage = () => {
const [msg, setMessage] = React.useState("");
const { socket, socketUpdated } = React.useContext(SocketContext);
console.log("Doctor socket", socket, socketUpdated);
React.useEffect(() => {
console.log("socket", socket);
if (socket !== undefined) {
console.log("socket join", socket);
socket.on("JoinAccept", message => {
debugger;
console.log("message", message);
setMessage(message);
});
}
}, [socket]);
return (
<div>
<h1>Doctor </h1>
</div>
);
};
server.js
io.of("/sockets").on("connection", socket => {
console.log("socket connection is made!!!", socket.id);
socket.on("clientData", clientData => {
console.log(clientData, "CLEITN DATA");
socket.emit("connected", "connected");
});
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
socket.broadcast.to(data.to).emit("JoinAccept", msg);
});
}
In my case the server gets Join event from patients and then sends event to requested doctor but doctor page is unresponsive. I mean doctor page does not listens the changes i.e it could not listens socket event JoinAccept so that it can join patient's request.
UPDATE
changing
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
socket.broadcast.to(data.to).emit("JoinAccept", msg);
});
to following works
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
io.of("/sockets").emit("JoinAccept", msg)
});
But I want to emit 'JoinAccept' event only for a particular doctor which patient has requested for from join events.
On DoctorPage.js socket is an object, if you put this object as the only variable in the dependency array it's going to run as a loop.
Try switching to the socket id instead, so:
React.useEffect(() => {
console.log("socket", socket);
if (socket !== undefined) {
console.log("socket join", socket);
socket.on("JoinAccept", message => {
debugger;
console.log("message", message);
setMessage(message);
});
}
}, [socket.id]);

Web-socket connection getting disconnected after one handshake in node.js and react

I am trying to build a simple Chatroom where multiple users can post the messages.I have setup the node web socket and it's broadcasting for a single message, but it closes the connection immediately after that.
This includes react on the front-end and node.js on the backend.
For simplicity i am storing and retrieving all the messages in a json file.
I am using ws: a node.js websocket library to setup the socket connection.
In client side i am using browsers WebSocket instance and listening on the same port.
// In server.js (Backend)
const server = http.createServer(doOnRequest)
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 1994 })
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN){
client.send(data);
}
});
});
ws.on('close', () => {
console.log('server conn closed');
setTimeout(() => {
console.log('closed by server console. log');
}, 2000);
})
});
function doOnRequest(request, response) {
// This functions handles all the get and post calls and stores
it in a file
}
const server = http.createServer(doOnRequest)
server.listen(3001);
// IN Chatroom.js ( UI )
const URL = 'ws://localhost:1994'
class Chatroom extends React.Component {
ws = new WebSocket(URL)
constructor(props) {
////// binding classes declaring state ////
}
componentDidMount() {
this.ws.onopen = () => {
console.log('connected')
}
this.ws.onmessage = evt => {
// on receiving a message, add it to the list of messages
let x = this.state.messageList;
x ? x.push(JSON.parse(evt.data)) : [JSON.parse(evt.data)];
this.setState({
messageList: x,
value: ''
});
}
this.ws.onclose = () => {
console.log('disconnected')
}
this.renderOlderMessages();
}
renderOlderMessages() {
// render older messages using GET request
}
addNewMessage(chatObj) {
// let res = a Post request (with Request body as chatObj).
res.then(() => {
this.ws.send(JSON.stringify(chatObj))
let x = this.state.messageList;
x ? x.push(chatObj) : [chatObj];
this.setState({
messageList: x,
value: ''
});
});
}
render() {
return (
<div>
/// render old messages ///
</div>
<div>
<form>
Input ---> New message ---> Submit
</form>
</div>
)
}
}

Categories

Resources