React not responding to emit from server [Socket IO] - node.js

Okay so i have no prior knowledge about Socket IO so please bear with me if my question i really stupid ;-; Any help at all is appreciated.
Basically what I'm trying to do is have my React front end get a 'signal' from the server. So I have an socket.emit('task-data-changed') server side and an 'io.on('task-data-changed)' on the front end but this does not seem to do anything and i have no idea why.
Here's my server side code:
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
io.origins('*:*');
io.on('connection', socket => {
console.log('a user connected');
socket.on("task-data", () => {
console.log('got task-data signal on the backend');
socket.emit("task-data-changed", 'everyone');
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
Here's my React code:
useEffect(()=> {
Socket.on("task-data-changed",() => {
console.log('task data was changed on the backend');
});
return () => Socket.off("task-data-changed");
},[]);
const submitNewTaskHandler = () => {
console.log('trying to emit task-data');
Socket.emit('task-data');
}
Things i know are working i guess:
The server logs 'a user connected' and 'user disconnected' appropriately when we load to localhost and when the page closes.
Inside 'submitNewTaskHander', the console.log() runs properly as well as the Socket.emit('task-data')
I know the above thing because the server is able to get the 'task-data' signal and then logs 'got task-data on the backend' properly.
the problem is after that the socket.emit('task-data-changed') on the server-side and the Socket.on('task-data-changed') don't seem to be working.
Thank you for reading so far, apologies if I've used the wrong terminology.

Your code to set the event listener on your front-end socket seems fine:
useEffect(()=> {
Socket.on("task-data-changed",() => {
console.log('task data was changed on the backend');
});
return () => Socket.off("task-data-changed");
},[]);
But, my question to you is, why is that code inside of useEffect()? You should put it wherever your Socket is originally made. Or, you could put it on the line just before Socket.emit('task-data');
To give some explanation, Socket.on() is a listener. In your case, it listens for 'task-data-changed'. Once you tell it to start listening, it will keep listening forever. So, you should just set the listener up once by using Socket.on(), and you usually do this right when you initialize the Socket. Just do it once and it will stay listening and running your callback function.

Related

socket.io | Should I wrap my route handlers inside io.on('connection')?

In the code below, I'm assuming there is a chance that my route handler will fire and try to emit to the socket before the io connection has been established:
server.js:
import { Server } from 'socket.io'
....
....
const app = express()
const io = new Server(....)
app.io = io
app.post('/something', (req, res) => {
req.app.io.emit('something', doSomethingWith(req.body))
res.status(200)
})
io.on('connection', function(socket) {
console.log('socket connected')
socket.on('disconnect', (reason) => {
console.log('disconnected due to = ', reason)
})
})
client.js:
socket = io(`http://localhost:${port}`, { transports: ['websocket'] })
socket.on('something', (data) => {
doSomethingMoreWith(data)
})
fetch('/something', ....)
In that case, is it safer to instead do:
io.on('connection', function(socket) {
app.post('/something', ....)
app.get('/something', ....)
.....
...
socket.on('disconnect', (reason) => {
console.log('disconnected due to = ', reason)
})
})
Or is this is not recommended and there is a better option ?
Putting app.post() and app.get() inside of io.on('connection', ...) is never the proper design or implementation. This is because io.on('connection', ...) is triggered multiple times and there's no point in adding the same express route handler over and over again as that will just waste memory and do nothing useful. The very first client to connect on socket.io would cause the routes to be registered and they'd be there from then on for all other clients (whether they connected via socket.io or not).
It is unclear why you are trying to do this. You don't install routes for one particular circumstance. Routes are installed once for all clients in all states. So, if you're trying to conditionally install routes, that type of design does not work. If you further explain what you're trying to accomplish, then perhaps we could make some different suggestions for a design.
In the code below, I'm assuming there is a chance that my route handler will fire and try to emit to the socket before the io connection has been established:
app.post('/something', (req, res) => {
req.app.io.emit('something', doSomethingWith(req.body))
res.status(200)
});
How exactly this code works depends upon what is doing the POST. If it's Javascript in an existing page, then that page will already by up and initialized and you control (with your Javascript client code in the page) whether you wait to issue the POST to /something until after the socket.io connection is established.
If this POST is a regular browser-based form submission (no Javascript involved), then you have other problems because a form submission from a browser reloads the current browser page with the response from the POST and, in the process of reloading the page, kills any existing socket.io connection that page had (since it loads a new page). Since you're not sending any content back from the POST, this would result in an empty page being displayed in the browser and no socket.io connection.
In looking at your client code, it appears that perhaps the POST is coming from a fetch() in the client code (and thus entirely Javascript-based). If that's the case, I would suggest restructuring your client code so that it waits until the socket.io connection has finished connecting before doing the fetch(). That way, you know you will be able to receive the io.emit() that the server does.
socket = io(`http://localhost:${port}`, { transports: ['websocket'] })
socket.on('something', (data) => {
doSomethingMoreWith(data)
});
socket.on('connect', () => {
// only issue fetch after socket.io connection is operational
fetch('/something', ....)
});

why socket connected twice in nodejs server with nextjs as the front end?

I hope to get some answers here as i have been trying to debug for few days now.
The expected behaviour:
socket in server connected once when user is in my website.
The actual behaviour:
socket in server connected twice when user is in my website.
I am using Next.js as the front end and node server as backend.
_app.js
const MyApp = () => {
useEffect(() => {
socket.once('connect', () => {
console.log('Connected');
});
return () => {
socket.disconnect();
};
}, []);}
server.js
let connections = [];
module.exports = function (io) {
io.on('connection', async (socket) => {
console.log(socket.id + ' connected', '\n');})}
after hours of research, i finally found the answer!
How To Implement React hook Socketio in Next.js
I followed the top answer and it worked! because nextjs render once in its server and its client.

One specific "socket.on()" doesn't work on client side

Server receive message from socket.emit("Wiadomosc"), but client doesn't,
but when I emit message from server io.emit("Wiadomosc"), client receive message;
There is no errors, others socket.emit() socket.on() works fine
I tried everything: copied everything from working functions and nothing.
I think that there is something wrong with me and not with code
Okey, client side:
socket = io.connect("localhost:4000", {
transports: ['websocket']
});
socket.on("Wiadomosc", function () {
console.log("wd");
});
socket.on("gotowosc", function () {
$(".gotowosc").css("display", "block");
});
//somewhere else in code
console.log(socket) //to see if it's fine, and it is
socket.emit("Wiadomosc");
the second one works fine
server side:
let express = require("express");
let app = express();
let server = app.listen(4000, function () {
console.log("listening on port 4000");
});
let socket = require("socket.io");
let io = socket(server);
io.on("connection", function (socket) {
socket.on("Wiadomosc", function () { //I did something like that to check if emit works, and it works
console.log("fine");
});
});
io.emit("gotowosc");
Emitting from a client sends to the server, not to any other client. If you want to send a message from one client to all clients, you send a message to the server and then code the server to receive it and send it to other clients.

Trigger `socket.io` event from server(`node.js`), not from client

I am trying to make a game server with node.js, socket.io.
The basic idea likes below.
Initialize socket.io instance when the server starts
Store instance in global scope, so controllers can access it
When API calls, we trigger some socket.io event in the controller or some other points
Here is the implementation I made ...
First, in server.js - entry point
let GlobalVars = require('./state/GlobalVars');
const apiRouters = require('./router');
...
app.use('/api', apiRouters);
app.get('/', (req, res) => {
res.sendFile(`${__dirname}/test/simpleClient.html`)
});
const httpServer = http.createServer(app);
let socketIOInstance = socketIO(httpServer);
socketIOInstance.on('connection', (socket) => {
console.log('SOCKET.IO A USER CONNECTED');
socket.on('create', (data) => {
console.log('SOCKET.IO create called', socket);
socket.join(data.room);
socketIOInstance.emit('message', 'New people joined');
});
socket.on('join', (data) => {
console.log('SOCKET.IO join called', data);
})
socket.emit('message', 'Hi');
});
GlobalVars.socketIO = socketIOInstance;
// Add to global, so the controllers can manage own actions like create, join ...
httpServer.listen(port, () => {
console.log(`Server Listening on the port ${port}`);
})
...
When I access from a client, I am able to see SOCKET.IO A USER CONNECTED and Hi in the browser console.
Second, In api controller.
let GlobalVars = require('../state/GlobalVars');
...
router.post('/create', (req, res) => {
console.log('GenerateGameSokect');
let game = new Game();
let gameId = game.gameId;
// console.log('Global vars ', GlobalVars.socketIO);
GlobalVars.socketIO.emit('create', {
room: gameId
});
res.json({
result : 'SUCCESS',
game : game
})
});
I imported GlobalVars which contains socketIO instance. So what I expected was, socket create event triggered from the statement GlobalVars.socketIO.emit('create', Object) but could not find message in the server logs.
I got no clue what I was missing.
The final form I pursue is something like...
When user call create API, I creates socket connection and room
API will called in HTTP protocol, but in the API, the server publishes some events. - pubsub like.
Thanks for reading my questions b. Here is full source code till now(bitbucket public)
================== EDIT ====================
I got understood (maybe...)
The user-flow I wanted was ...
The client call API
(In the server) Checking validation in API and if valid emit to socket.io
If event accepted send new status to all clients
However, creating socket.io connection in the server looks strange for me, the solution is up to the client.
New user-flow I will change
The client call a validation API
If return is valid, the client emit socket.io event. This time server only do validation, not emit socket.io
In socket event, send new status to all other users
================== EDIT #2 ====================
This is a kind of conclusion. It looks I just misunderstanding the concept of socket communication. Like answer and replies say, Socket and HTTP are totally different channels, there is no way to connect both. (At least, without open new connection from http server to socket)
If this is wrong, you could add reply, Thanks
Now I understand you. Or at least I think!
Let's put it this way: there are two (asymetric) sides on a socket, server and client. What I called, respectively, "global manager" and "socket" in my comment to your post.
const server = require('socket.io')(yourHttpServer);
// client is installed as well when `npm i socket.io`
const client = require('socket.io-client')('http://localhost:' + yourServerPort);
// `socket` is the server side of the socket
server.on('connection', (socket) => {
// this will be triggered by client sides emitting 'create'
socket.on('create', (data) => {
console.log('a client socket just fired a "create" event!');
});
});
// this will be triggered by server side emitting 'create'
client.on('create', (data) => {
server.emit('create', {content: 'this will result in an infinite loop of "create" events!'});
});
In your /create route, when you GlobalVars.socketIO.emit('create', ...), the server-side socket handler isn't triggered, however if you have clients connected through a browser (or, like I showed above, if you connect a client socket directly from the server) then these will trigger their 'create' listener, if any.
Hope this helps you get on the right tracks!

Socket io on connection not connecting NodeJS

I just started learning NodeJS and I am trying to make a simple server-client project using Socket io.
What happens right now is that when I open localhost:8001, I don't see any logs inside the listener.sockets.on block.
var http = require('http');
var app = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('<h1>Hello!</h1>');
}).listen(8001);
var io = require('socket.io');
var listener = io.listen(app);
console.log("Sample Socket IO");
listener.sockets.on('connection', function (socket) {
console.log('a user connected');
socket.emit('connected', 'Welcome');
socket.on('disconnect', function () {
console.log('user disconnected');
});
});
It looks like the logging will occur when a connection happens. You setup a listening socket, so try to connect to it. Try 'telnet 127.0.0.1 8001' to connect.
The browser page needs to load the socket.io client code for one thing. That is the first thing missing that I can see. Look through the example here http://socket.io/get-started/chat/ and make sure you are following exactly at first and then make changes after you get that example working. Your server code looks a bit different from their example also.

Resources