socketio cant get new messages from server-side | react - reactjs

emitting messages to server working fine but when i try to get message from server its not working.
tried different useEffect dependencies (incomingMessage etc.), after some search all sources using socket as dependency.
in server side messages is appear tring to send message to client
client
import io from "socket.io-client";
import { useEffect, useState } from "react";
const socket = io("http://localhost:3001");
function App() {
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState("");
const [incomingMessage, setIncomingMessage] = useState([]);
useEffect(() => {
socket.on("connect", () => {
setLoading(false);
});
},[]);
const sendMessage = (e) => {
e.preventDefault();
socket.emit("message", message);
setMessage("");
};
useEffect(() => {
socket.on("new-message", (data) => {
console.log("new message", data);
setIncomingMessage(data);
});
console.log("incoming message", incomingMessage);
}, [socket]);
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<h1>{socket.id}</h1>
<input value={message} onChange={(e) => setMessage(e.target.value)} />
<button onClick={sendMessage}>Send</button>
<p>{incomingMessage}</p>
</div>
);
}
export default App;
server
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
socket.on("message", (data) => {
console.log(data);
socket.emit("new-message", data);
});
});
httpServer.listen(3001);

Related

Socket does not get triggered in my UseEffect. - React, socket.io, socket.io-client MERN

I am trying to build a simple chat app, but I can't seem to get my socket emit to get triggered in my useEffect. I can send messages fine and but on my second user's window it does not seem to be receiving it.
Chat.jsx
import React, { useState, useContext, useEffect } from "react";
import { UserContext } from "../context/UserContext";
import logo1 from "../images/logo1.png";
import io from "socket.io-client";
import { useNavigate } from "react-router-dom";
import aolemoji from "../images/aolemoji.png";
const Chat = (props) => {
function helper(message, object){
console.log(message,object);
return object;
};
const { user, setUser, socket } = useContext(UserContext);
const [messages, setMessages] = useState([]);
const [currentMessage, setCurrentMessage] = useState("");
useEffect(() => {
console.log("wheeeeeee4e")
socket.on("private_message_response", (data) => {
console.log("what is my socket", socket);
console.log("Got your message");
console.log("user should have have received data and confirm", data);
setMessages((prevState) => [...prevState, data]);
});
return () => setUser({ ...user});
}, []);
console.log("console io", io);
const navigate = useNavigate();
const sendMessage = (e) => {
e.preventDefault();
console.log("Sending private message");
socket.emit("private_message", helper("testing send",
{
user: user.screenName,
// room: user.room,
message: currentMessage,
}), (response)=>{
console.log(response);
});
};
const awayMessageButtonHandler = () => {
navigate("/awayMessages");
};
const handleLogOutClick = () => {
console.log(`${user.screenName} has been logged out.`);
alert(`${user.screenName} has been successfully logged out! 👋`);
navigate("/");
};
Loop through messages...
{messages.map((m, i) => <p className="text-black" key={i}> {m.user}: {m.message}</p>))}
Here's my server.js
const express = require('express');
// const http = require('http');
const app = express();
const cors = require('cors')
const cookieParser = require('cookie-parser');
const dotenv = require('dotenv');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const myFirstSecret = process.env.FIRST_SECRET_KEY;
const http = require('http');
const port = 8000;
const server = app.listen(port, () => console.log("Listening on port", port));
const socketio = require('socket.io')
require('./config/mongoose.config')
app.use(cors({credentials: true, origin: 'http://localhost:3000'}));
app.use(express.json());
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));
require("./routes/awayMessage.routes")(app);
const UserRoutes = require('./routes/user.routes')
UserRoutes(app);
const io = socketio(server,{
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
allowedHeaders: ['*'],
credentials: true
}
})
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on("connection", (socket) => {
console.log(" server io ...on")
console.log("Socket:", socket.id, "connected to the server");
socket.on("send_message", (data) => {
console.log(data);
io.emit("message_received", data)
console.log("io:", io);
console.log("emit:", emit);
})
socket.on("join_room", (data) => {
console.log("Joined room:", data);
socket.join(data)
})
socket.on("private_message", (data) => {
console.log(data);
io.to(data.room).emit("private_message_response", data)
})
UserContext.jsx
import {useState, createContext} from 'react';
import io from 'socket.io-client';
const UserContext = createContext();
const UserProvider = ({children}) => {
const [user, setUser] = useState({
id: 0,
screenName: "",
room: ""
})
const [socket] = useState(() => io(":8000"))
return (
<UserContext.Provider value={{user, setUser, socket}}>
{children}
</UserContext.Provider>
)
}
export {UserProvider, UserContext}
Can anyone help me with this? Thank you in advance! :)

Getting duplicate messages with React + Socket.IO chat app

Attempting to create a simple React chat app, but I'm running into an issue where duplicate messages appear. I run the server with node server.js, in a separate terminal run the client with npm start, then open two windows at localhost:3000. I can send a message, but any message I send is received x2. For example (after submitting the form with "i only sent this once" on one window, this is what appears in the second window).:
I did console.log on the server-side, and only one message is being received from the server.
Here is my server.js file:
const express = require("express");
const socketio = require("socket.io");
const http = require("http");
const cors = require("cors");
const PORT = process.env.PORT || 5001;
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
io.on("connection", (socket) => {
socket.on("connection", () => {});
socket.on("send-message", (message) => {
console.log(message);
socket.broadcast.emit("message", message);
});
});
Here's my socket.js file:
import io from "socket.io-client";
const SERVER = "http://127.0.0.1:5001";
const connectionOptions = {
forceNew: true,
reconnectionAttempts: "Infinity",
timeout: 10000,
transports: ["websocket"],
};
let socket = io.connect(SERVER, connectionOptions);
export default socket;
Here's my App.js file:
import { useState, useEffect } from "react";
import "./App.css";
import socket from "./socket";
const ChatWindow = () => {
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const handleMessageChange = (event) => {
setMessage(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
socket.emit("send-message", { message: message });
};
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
}, []);
return (
<div>
{messages.map((message) => {
return <h3>message: {message}</h3>;
})}
<form onSubmit={handleSubmit}>
<input value={message} onChange={handleMessageChange}></input>
<button type="submit">Send Message</button>
</form>
<h1>Chat Window</h1>
</div>
);
};
function App() {
return (
<div className="App">
<ChatWindow />
</div>
);
}
export default App;
As punjira answered in a comment, removing the event listener for the useEffect worked.
Specifically, before:
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
}, []);
After:
useEffect(() => {
socket.on("connection", null);
socket.on("msg", (payload) => {
setMessages((prev) => {
return [...prev, payload.msg];
});
});
return function cleanup() {
socket.removeListener("msg");
};
}, []);
If you are using UseEffects Hooks you need to clean up
Before :
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
}, []);
You can add this line to clean up :
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
return () => socket.off("message"); // add this line to your code
}, []);

socket.io post message twice (react)

sometimes its post 3-4 times.
probably its about rendering. i moved init function from container.jsx to app.jsx not worked.
tried to delete cache. not worked
as you can see from console log. is chatlist rendered twice? or something like that
using socket.broadcast.emit on backend. not working
client code
import { useEffect } from "react";
import io from "socket.io-client";
let socket;
export const init = () => {
console.log("Connecting...");
socket = io.connect("http://localhost:3000", {
transports: ["websocket"],
});
socket.on("connect", () => console.log("Connected!"));
};
export const sendMessage = (message) => {
if (socket) {
socket.emit("new-message", message);
}
};
export const onNewMessage = (callback) => {
if (!socket) return;
socket.on("receive-message", (message) => {
callback(message);
console.log("Received message: ", message);
});
};
server code
const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const cors = require("cors");
const Messages = require("./lib/Messages");
app.use(cors());
app.get("/", (req, res) => {
res.end("Merhaba Socket.IO");
});
io.on("connection", (socket) => {
console.log("a user connected");
Messages.list((data) => {
console.log(data);
socket.emit("message-list", data);
});
socket.on("new-message", (message) => {
console.log(message);
Messages.upsert({ message });
socket.broadcast.emit("receive-message", message);
});
socket.on("disconnect", () => console.log("a user disconnected"));
});
http.listen(process.env.PORT || "3000", () => {
console.log("listening on *:3000");
});
chatlist.jsx
import { useChat } from "../context/ChatContext";
import styles from "./style.module.css";
import ChatItem from "./ChatItem";
export default function ChatList() {
const { messages } = useChat();
console.log(messages);
return (
<div className={styles.chatlist}>
<div>
{messages.map((item, key) => (
console.log("Message: ",item),
<ChatItem key={key} value={item} />
))}
</div>
</div>
);
}
its because of the useEffect bug in last version of react

How to prevent react is rendering 2 times

I am facing a problem where the react is rendering 2 times but I do not see any problem in the code or it is come from the socket? I noticed this problem when consoling and when displaying data, the data duplicate. Below I put my client react code with the server socket code.
React client
import "./App.css";
import io from "socket.io-client";
import { useEffect, useState } from "react";
const socket = io.connect("http://localhost:3001");
function App() {
const [text, setText] = useState("");
const [room, setRoom] = useState("");
const [data, setData] = useState([]);
const [action, setAction] = useState(true);
console.log("data", data);
console.log(text);
console.log(room);
const sendMessage = () => {
socket.emit("send_message", { text, room });
};
const joinRoom = () => {
if (room !== "") {
socket.emit("join_room", room);
}
};
useEffect(() => {
socket.on("receive_message", (msg) => {
setData((prev) => [
...prev,
{
message: msg,
},
]);
});
}, [action]);
return (
<div className="App">
{data.map((da) => (
<p>{da.message}</p>
))}
<input
type="text"
placeholder="room no"
onChange={(e) => {
setRoom(e.target.value);
}}
></input>
<button onClick={joinRoom}>Join</button>
<br />
<input
type="text"
placeholder="message .."
onChange={(e) => {
setText(e.target.value);
}}
></input>
<button onClick={sendMessage}>Send Message</button>
</div>
);
}
export default App;
Socket server
const express = require("express");
const app = express();
const http = require("http");
const { Server } = require("socket.io");
const cors = require("cors");
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);
})
socket.on("send_message", (data) => {
socket.to(data.room).emit("receive_message", data.text);
})
});
server.listen(3001, () => {
console.log("SERVER OK");
});

emitting action with socketio into different react components

I am trying to emit a msg into different react components and update all components with new value.
server:
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const {addPlayer, removePlayer, getPlayer, getPlayerssInRoom, addRoom} = require('./players');
const cors = require('cors');
const PORT = process.env.PORT || 5000;
const router = require('./router');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
app.use(router);
io.on('connection', (socket) => {
socket.on('question', (question) => {
socket.emit('setQuestion', question);
socket.broadcast.to('rtd').emit('setQuestion', question);
io.sockets.in('rtd').emit('setQuestion', question);
});
socket.on('disconnect', () => {
console.log('User had left');
});
});
app.use(router);
server.listen(PORT, () => console.log(`Server has started on port ${PORT}`));
First component where I emit an action to the server:
import React, {useEffect, useState} from 'react';
import Button from 'react-bootstrap/Button';
import io from 'socket.io-client';
let socket;
const Admin = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
const ENDPOINT = 'localhost:5000';
socket = io(ENDPOINT);
const publishQuestion = () => {
console.log('trying');
socket.emit('question', currentQuestion);
}
useEffect(() => {
socket = io(ENDPOINT);
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
}, [currentQuestion]);
return (
<div className="justify-content-center">
<h1>Admin</h1>
<input onChange={(event)=>setCurrentQuestion(event.target.value)}/>
<Button onClick={()=> publishQuestion()}>Publish word</Button>
</div>
)
}
export default Admin;
Second component where I am having issue to listen to the emit that comes from the server:
import React, {useState} from 'react';
import io from 'socket.io-client';
let socket;
const Player = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
const ENDPOINT = 'localhost:5000';
socket = io(ENDPOINT);
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
return (
<h1>player</h1>
)
}
export default Player;
Anyone has an idea why the Player component not picking on the server emit (socket.on('setQuestion', (newQuestion))?
I recommend you to keep the socket client reference in one place. This way you have the same socket reference in your components and the components state updates won't affect it:
socket-client.js
import io from "socket.io-client";
const ENDPOINT = "localhost:5000";
export const socket = io(ENDPOINT);
Admin.js
import { socket } from "./socket-client";
const Admin = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
const publishQuestion = () => {
socket.emit('question', currentQuestion);
}
useEffect(() => {
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
}, []); //--> empty dependency
}
Player.js
import { socket } from "./socket-client";
const Player = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
useEffect(()=> {
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
}, [])
}
EDIT /
Use io.sockets.in if you want to emit to all socket in that room.
Use socket.broadcast.to to emit to all socket in that room except it.
server.js
io.on("connection", (socket) => {
socket.join('rtd'); //--> join to rtd room;
socket.on("question", (question) => {
// socket.broadcast.to("rtd").emit("setQuestion", question);
io.sockets.in("rtd").emit("setQuestion", question);
});
socket.on("disconnect", () => {
console.log("User had left");
});
});

Resources