Reconnecting to socket channel after disconnection in react - javascript

There's various answers to this question, but either they're outdated (I don't think the "reconnect" event exist anymore?) or just doesn't work for me.
I have a ton of data that the client is waiting for from the server via socket.io sockets. It's fine until 10-15 minutes later with over 1600 results that the socket reconnects. After the reconnection happens, I do not get anymore of the data that the server emits, which I assume is because I've lost the events from the original socket connection.
I have to refresh to continue getting that data.
How do I reconnect to socket.io after every reconnection?
Client:
socket.js (context)
import { createContext } from 'react';
import io from 'socket.io-client';
export const socket = io('http://localhost:5000');
export const SocketContext = createContext();
Layout.js
import { socket, SocketContext } from '../context/socket'
function MyApp({ Component, pageProps }) {
return <SocketContext.Provider value={socket}>
<Layout>
<Component {...pageProps} />
</Layout>
</SocketContext.Provider>
}
page (next.js)
import { SocketContext } from '../context/socket';
...
const socket = useContext(SocketContext);
useEffect(() => {
socket.emit('joined', socketChannel);
socket.on(socketChannel, handleStream);
}, []);
Server:
index.js (Uses fastify-socket.io)
fastify.io.on('connection', socket => {
socket.on('joined', (channel) => {
socket.join(channel);
})
});
redis.subscriber.on('message', (channel, message) => {
io.to(channel).emit(channel, message);
});

Socket.io will automatically leave the rooms when you have a disconnect.
Rooms are left automatically upon disconnection.
in useEffect(), you are joining the room once:
socket.emit('joined', socketChannel);
socket.on(socketChannel, handleStream);
Two options:
Automatically join at connect on client:
useEffect(() => {
socket.on(socketChannel, handleStream);
// connect fires when connected, also after reconnected
socket.on('connect', () => {
console.log('connect', socket.connected);
// automatically join the room
socket.emit('joined', socketChannel);
});
}, []);
Automatically join at the server, but this assumes all clients join the relevant room which may not be suitable:
io.on('connection', (socket) => {
console.log(`${socket.id} connected`);
// automatically join the room
socket.join(socketChannel);
...

Related

How to use STOMP for connecting with ActiveMQ in React js

Can anyone provide successful implementation of Stomp using the latest version of ActiveMQ 5.x using the React N? How to connect and publish to the queue?
I have below questions:
I have to retrieve the data from consumer and do some add some boolean value and send it to publish.
How can I keep the connection alive because continuously I will get message in queues.
How can I implement this in React.js in simple manner (any plugins)
I tried with JavaScript, and it works as expected.
consumer.js
const Stomp = require("stomp-client");
const stompClient = new Stomp("127.0.0.1",61613);
stompClient.connect( function(sessionId){
console.log("consumer connected");
stompClient.subscribe("/queue/<name>",function(body){
console.log(body);
});
});
producer.js
const Stomp = require("stomp-client");
const stompClient = new Stomp("127.0.0.1",61613);
stompClient.connect( function(sessionId){
console.log("producer connected");
stompClient.publish("/queue/<name>",function(body){
console.log(body);
console.log(typeof(body));
//JSON.stringify(body);
});
stompClient.disconnect();
});
This is what I tried in React.JS (which failed): here i can able to connect and after that if i call subscribe with que name it is not giving any response
import './App.css';
import React,{useEffect} from 'react';
import { Client, Message } from '#stomp/stompjs';
function App() {
const clientdata = new Client();
useEffect(() => {
clientdata.configure({
brokerURL: 'ws://localhost:61614/stomp',
onConnect: (frame) => {
console.log('onConnect');
console.log(frame);
clientdata.subscribe('/queue/<quename>',info => {
console.log(info);
})
console.log(subscription);
},
// Helps during debugging, remove in production
debug: (str) => {
// console.log(new Date(), str);
}
});
client.activate();
}, []);
return (
<div >
</div>
);
}
export default App;
When i tried the above code I am getting only connected log and I'm not able to subscribe any thing and not seeing anything.

How do I start websocket server (socketIO or otherwise) in Nuxt 3? Does not work the same as in Nuxt 2

I am trying to convert my code from Nuxt 2 to Nuxt 3, and I have run into an issue with creating a websocket server in Nuxt 3.
It works perfectly fine in Nuxt 2 using this code:
// Nuxt 2: modules/socket.js
import http from 'http'
import socketIO from 'socket.io'
export default function () {
this.nuxt.hook('render:before', () => {
const server = http.createServer(this.nuxt.renderer.app)
const io = socketIO(server)
this.nuxt.server.listen = (port, host) => new Promise(resolve => server.listen(port || 3000, host || 'localhost', resolve))
this.nuxt.hook('close', () => new Promise(server.close))
io.on('connection', (socket) => {
console.log("CONNECTED")
})
})
}
// Nuxt 2: plugins/socket.client.js
import io from 'socket.io-client'
const socket = io('http://localhost:3000')
export default ({}, inject) => {
inject('socket', socket)
}
<!-- Nuxt 2: pages/index.vue -->
<template>
<div>
<p>Check socket status in Vue devtools...</p>
</div>
</template>
<script>
export default {
computed: {
socket() {
return this.$socket ? this.$socket : {};
}
}
}
</script>
However, in Nuxt 3 I cannot access this.nuxt.renderer.app in the modules/socket.js file (for http.createServer(...)), and I cannot figure out how to access the correct renderer.app elsewhere in a Nuxt3 module. My Nuxt 3 code looks like this:
// Nuxt 3: modules/socket.js
import http from 'http'
import socketIO from 'socket.io'
export default (_, nuxt) => {
// Note that I use the 'ready' hook here - render:before is apparently not included in Nuxt3.
nuxt.hook('ready', renderer => {
// nuxt.renderer is undefined, so I've tried with renderer.app instead, with no luck.
const server = http.createServer(renderer.app)
const io = socketIO(server)
nuxt.server.listen = (port, host) => new Promise(resolve => server.listen(port || 3000, host || 'localhost', resolve))
nuxt.hook('close', () => new Promise(server.close))
io.on('connection', () => {
console.log("CONNECTION")
})
})
}
// Nuxt 3: plugins/socket.client.js
import io from 'socket.io-client'
export default defineNuxtPlugin(() => {
const socket = io('http://localhost:3000')
return {
provide: {
socket: socket
}
}
})
<!-- Nuxt 3: app.vue -->
<template>
<div>
<p>Check socket status in Vue devtools...</p>
</div>
</template>
<script setup>
const { $socket } = useNuxtApp()
</script>
I would make a codesandbox link for you, but every time I try, it breaks before I even add any code. I think it does not correctly work with Nuxt3 yet.
Has anyone successfully established a websocket server in a Nuxt 3 module yet? Or can anyone see what I am missing?
I am interested in any working solution, it does not necessarily have to be socket.io.
I figured it out!
Digging deep into the Nuxt 3 code, it turns out that they have a listen hook which provides the server parameter that I needed to set up the server. This information is really hard to find though.
I also managed to simplify the script a bit.
Here's the updated modules/socket.js:
import { Server } from 'socket.io'
export default (_, nuxt) => {
nuxt.hook('listen', server => {
const io = new Server(server)
nuxt.hook('close', () => io.close())
io.on('connection', () => {
console.log("CONNECTION")
})
})
}
Everything else can remain the same
thx anbork I'm use ws it the same method create file "modules/wsserver.ts"
import { WebSocketServer } from "ws"
export default (_: any, nuxt: any) => {
nuxt.hook("listen", (server: any) => {
const wss = new WebSocketServer({ server })
nuxt.hook("close", () => wss.close())
wss.on("connection", function connection(ws) {
ws.on("message", function message(data) {
console.log("received: %s", data)
})
ws.send("something")
})
})
}
then register at nuxt.config
modules: ["./modules/wsserver"],
Based on #ahbork's response and a addition from the Nuxt 3 docs, I got this on Vue 3 + Nuxt 3 + Typescript:
import { Server } from 'socket.io'
import { defineNuxtModule } from '#nuxt/kit'
export default defineNuxtModule({
setup(options, nuxt) {
nuxt.hook('listen', (server) => {
console.log('Socket listen', server.address(), server.eventNames())
const io = new Server(server)
nuxt.hook('close', () => io.close())
io.on('connection', (socket) => {
console.log('Connection', socket.id)
})
io.on('connect', (socket) => {
socket.emit('message', `welcome ${socket.id}`)
socket.broadcast.emit('message', `${socket.id} joined`)
socket.on('message', function message(data: any) {
console.log('message received: %s', data)
socket.emit('message', { data })
})
socket.on('disconnecting', () => {
console.log('disconnected', socket.id)
socket.broadcast.emit('message', `${socket.id} left`)
})
})
})
},
})

My useState in React/NEXTjs is not appending the result from a socket.io event from the server, just overwriting it

Frameworks used: Next.js, Socket.io, React
I am making a simple messaging app. I am basically just emitting a message someone types, sending that message to the server, which is then "broadcasted" back through an event called "receive-chat-message". The issue I'm having is when the response is handled on the front end with "receive-chat-message", the [messages] state is not appending a new message to display, it's just overwriting the state.
My goal is to send a message to the server; the server then sends the message back to the client. I then append the new message on the client, sent from the server, to the [messages] state on the client, and then render the state of [messages]. The problem is it's only overwriting the [messages] state, and not appending new messages to it.
Code that sends message to the server
const [message, setMessage] = useState("");
const handleChange = (e) => {
setMessage(e.target.value);
};
const submitMessage = async () => {
// socket.io
const socket = io();
socket.emit("send-chat-message", message, user);
setMessage("");
};
Front-End Code
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = io();
socket.on("receive-chat-message", handleSetMessage);
return () => socket.disconnect();
}, []);
const handleSetMessage = (data) => {
/* data prop is the new message - it has a {message: "message", user: "name"}
sent from server.
THIS IS THE PROBLEM IN THE CODE WHERE IT DOESN'T APPEND
NEW MESSAGES TO THE [messages] STATE, IT JUST OVERWRITES
THE CURRENT STATE WITH THE NEW MESSAGE. */
setMessages([...messages, data]);
};
Back-End Code
export default async function handler(req, res) {
if (!res.socket.server.io) {
const io = new Server(res.socket.server);
res.socket.server.io = io;
io.on("connection", async (socket) => {
socket.on("send-chat-message", async (message, user) => {
socket.broadcast.emit("recieve-chat-message",
{
message,
name: user && user.name,
});
});
});
}
}
I solved this by setting messages through the functional method like:
setMessages((messages) => [...messages, data]);
Instead of:
setMessages([...messages, data])
I think we need this because I was updating the state from a function called within a socket.io listener and was never actually grabbing the previous state; so I had to direct it to the previous state and then merge the values.

Client to retrieve data from backend server database table via a websocket connection

I am using the following server code to retrieve data from a postgres db:
const express = require('express')
const app = express()
const server = require('http').createServer(app);
const pool = require("postgresql");
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server:server });
const getTempData = async () => {
try {
const tempData = await pool.query("select country, temp from my_temp_table");
return JSON.stringify(tempData.rows)
} catch(err) {
console.error(err.messasge);
}
}
wss.on('connection', async (webSocketClient) => {
console.log('A new client Connected!');
const tempDetails = await getTempData();
webSocketClient.send(tempDetails);
webSocketClient.on('message', (message) => {
console.log('received: %s', message);
});
});
server.listen(3000, () => console.log(`Listening on port :3000`))
Now on the client side, I have created the following websocket connection to localhost 3000.
When first rendering the below client code, the data displays where I also get all the console log messages, i.e. ws opened, getting data.... and finally console logging the actual data.
isPaused is also set to false.
The problem I'm facing and unsure what the issue is, is that I expected to see my client page update the country/temp data (no page refresh), when I updated the country/temp values in my_temp_table database table, but it didn't.
The result that I expected was that via the websocket, anytime my table on the server-side updated, the client would update the tempData, via the second useEffect hook below.
I basically would like the client to pull in and display changes from the server via websocket when the data changes in the backend db table.
import React, { useState, useEffect, useRef } from 'react';
export default function Temperature() {
const [isPaused, setPause] = useState(false);
const [tempData, setTempData] = useState([]);
const [name, setName] = useState(null);
const ws = useRef(null);
useEffect(() => {
ws.current = new WebSocket("ws://localhost:3000");
ws.current.onopen = () => {
console.log("ws opened");
}
ws.current.onclose = () => console.log("ws closed");
return () => {
ws.current.close();
};
}, []);
useEffect(() => {
if (!ws.current) return;
ws.current.onmessage = e => {
if (isPaused) return;
console.log("getting temp data....");
const data = JSON.parse(e.data);
setTempData(data)
console.log("data: ",data);
};
}, [isPaused]);
return (
<div>
<button onClick={() => setPause(!isPaused)}>
{isPaused ? "Resume" : "Pause"}
</button>
{ tempData?
tempData.map((data, i) => (
<div>
<span>{data.country}</span>
<span>{data.temp}</span>
</div>
))
: null }
</div>
)
}
The code is executing only once because there are no recurrying calls to the web socket send event. When the web socket is created it gets the data from the database and sends it, and thats it.
You probably want some kind of action that triggers this event multiple times. For example, in your code:
wss.on("connection", async webSocketClient => {
console.log("A new client Connected!");
setInterval(() => {
const timeNow = Date.now();
webSocketClient.send(
JSON.stringify([
{ country: "country-a", temp: timeNow },
{ country: "country-b", temp: timeNow },
])
);
}, 1000);
webSocketClient.on("message", message => {
console.log("received: %s", message);
});
});
I see you are using some package to pool from a PostgreSQL db. Take a look at this other example.
How would your clients know if there is any change in database on server side ?
You can create an event that triggers each time a particular data changes and listen to those event on your client sockets. Like you did with onmessage event in your current code.
You can render the react component based on this event.

keep socket alive when page/browser is idle

I am using socket.io for real time communication. I am facing problem when user's browser is idle. I mean, suppose there is two entity 'User A' and 'User B'. User A wants to talk with User B. If User B has opened the application but is not active on that page then User A cannot contact User B because the socket of User B is disconnected after some minutes when the system is idle. Also I don't see continuous polling on ws tab inside network
Here is the code to configure socket. I am using context API
const SocketProvider = ({ children }) => {
const [socketClient, setSocketClient] = React.useState();
const [socketUpdated, setSocketUpdated] = React.useState(false);
React.useEffect(() => {
const socket = io(SOCKET_URL, {
path: "/socket.io"
});
setSocketClient(socket);
// return () => {
// io.disconnect();
// };
}, []);
React.useEffect(() => {
if (socketClient !== undefined) {
if (user) { // from localstorage
console.log("user", user);
socketClient.on("connect", () => {
socketClient.emit("userData", {
_id: user._id,
socketId: socketClient.id
});
});
// socketClient.on("connected", msg => {
// console.log("connected msg", msg);
// setSocketUpdated(true);
// });
// setSocketUpdated(true);
}
}
}, [socketClient]);
return (
<>
<SocketContext.Provider value={{ socket: socketClient, socketUpdated }}>
{children}
</SocketContext.Provider>
</>
);
};
export { SocketContext, SocketProvider };
I want socket on multiple pages so for single socket reference I have used socket in context. Wherever I need socket, I import this context and use it accordingly.
How can I handle disconnection issue so the socket is still alive when user's system/browser is idle?

Categories

Resources