Prevent duplicate socket message using rxjs/redux (React, socket.io) - reactjs

I have a react application that connects to a socket and gets a list of data (initial data) from one of the channel
the issue appears while I got the list again when reconnecting or when I change the route (refresh is of course not a problem)
and then I got the list again and need to update the state again with all the list (I am doing that with REDUX)
My question i how can I check or prevent from update the state again . is there any subject that supports that? or i should check if the whole list exists in the reducer?
is it also a good solution to set a state in redux that said "fetched:true" and then don't dispatch in that case
listen to the channel: (class that listen to socket server create an observable using rxjs)
socketService.server.on('list', (res: any) => {
console.dir(res);
subject$.next(res)
});
update the state in redux-thunk action:
.subscribe(vall).... {
disptach(list)....
});
socketService only connect to the socket and return a socket

If you only need the value from the socket initially you should not read from it after you receive it initially.
You can either unsubscribe and not listen for any more messages (after the first one) or you can only get process the first message with rxjs:
// you can check here a condition for res if you need to
// (that it is not empty for example)
.pipe(first(res => res))
or
.pipe(take(1))
This will ensure you only read and dispatch for the first message you receive from that connection.

Related

redux dispatcher doesnt set some specific messages from socket (signalR)

i open the connection in a parent component above all and when a message comes i set it via redux and use it in another component. It's all good till messages come together(like glued) as shown in photo. when messages are received together redux sets one of two received messages. how do i overpass this issue that redux could handle each of socket's messages.
await hubConnection.start()
.then(()=>{
hubConnection?.on("ReceiveOrderEvents", (messageStreamItem: any) => {
console.log(messageStreamItem,'messageStreamItem')
dispatch(orderUpdateSocket(messageStreamItem));
});
})
after couple of days figuring out what the origin of problem was, i came up with an idea this solution:
since socket might send multiple messages very fast and in a very short time, redux might not handle it very well. messages from socket could be stored in an array and via forEach you could dispatch every one of them inside a setTimeOut function to proceed with other things you are about to do.
await hubConnection.start()
.then(()=>{
hubConnection?.on("ReceiveOrderEvents", (messageStreamItem: any) => {
let msg=[];
msg.push(messageStreamItem);
setTimeOut(()=>{
msg.forEach((message:any)=>dispatch(setter(messageStreamItem)));
msg = []
},[50])
});
})

Adding web-notification in React app similar to Alert but Not Alert

user story:
We need to alert the user when there a modification or a comment on the thing he has posted by another user.
We are new to react and we are lost in a loop about how to notify the user who is logged in on a different page (maybe his dashboard). The event is performed by another user2 where he is on a page where is commenting on the thing posted by user1.
How can we send an alert to that user1? who currently on a different page saying "there is a new comment on your post".
The issue we are thing about is: The event happens on the comment page. We can send the alert on that comment page. But how will we be able to send a notification to other users on a different page?
I know there is already present. You can take Jiira Board as an example.
Could anyone let us know how can we implement this?
One option is to use WebSockets. Whenever a user is on a page where you'd want them to be able to get a notification, open a websocket to your server:
const socket = new WebSocket('https://my-server.com/socket');
On your server, set up the socket endpoint. Whenever one user sends a message to another, on the server, for all sockets currently opened by the receiver, send a socket message informing them of the new message. Eg, on the server:
activeSockets
.filter(({ userId, socket }) => userId === receiverId)
.forEach(({ socket }) => {
socket.send('You have a new message');
});
And listen for those messages on the client:
socket.addEventListener('message', ({ data }) => {
if (data === 'You have a new message') {
alert(data);
}
});
This is, in broad terms, the industry standard for this sort of thing; it's what Stack Exchange does. It's how many websites allow the server to send data to the client without (inelegant) polling.

Best Way to Implement Notification System with Express.js, React, and Socket.io?

I'm working on a project that allows users to create projects and invite other users to join their projects. I would like to implement a real-time notification system for this but I'm not sure what's the best way to implement it. The way I'm doing it right now is I have a component connect to /ws-notifications namespace on mount and then join a room that's unique to them (im just using their mongodb Id as the room name).
/********************** SERVER - app.js *************************/
var notifications = io.of('/ws-notifications')
.on('connection', (socket)=>{
//connects each user to a room specific to them to listen for notifications
var room = socket.handshake.query.user;
socket.join(room);
...
});
Then when the project owner sends a user an invite, I emit to that user an event called "new-notifications".
/*************** CLIENT - InviteModal.js ***************/
class InvitationModal extends Component {
...
componentDidUpdate(){
if(!this.props.loading)
document.getElementById('submit-button').disabled = false;
//invitation was sent successfully created, clear form data
//and send the signal to the intvitee via socket io
if(!this.props.loading && this.props.successMessage){
//this.state.id holds the invitee's id
if(this.state.id)
this.state.socket.emit("send-notification", this.state.id)
//clear invite success message and other state after timeout
setTimeout(this.props.clearProcessData, 3000);
}
}
}
/****************** SERVER - app.js *********************/
var notifications = io.of('/ws-notifications')
.on('connection', (socket)=>{
//connects each user to a room specific to them to listen for notifications
var room = socket.handshake.query.user;
socket.join(room);
socket.on('send-notification', (user)=>{
socket.to(user).emit('new-notification');
})
});
Back on the invitee's page, when this event is emitted I just make a call to the api through an action to get all notifications for that user.
/********************** CLIENT - Nav.js ***********************/
class Nav extends Component {
constructor(){
super();
this.state = {
socket: null
}
}
componentDidMount(){
this.props.getNotifications();
this.setState({...this.state, socket: io('http://localhost:8000/ws-notifications?user=${this.props.user._id}')})
}
...
componentDidUpdate((prevProps, prevState){
//prevents checks if socket is already connected to prevent creating multiple unnecessary connections
if(this.state.socket && !prevState.socket){
//listens for new notifications
this.state.socket.on("new-notification", ()=>{
//dispatches action to get notifications from api
this.props.getNotifications();
});
}
}
}
I believe that's all the relevant code. If there's any more context needed please let me know and ill add it. Doing it this way works as intended but I want to know if there is maybe a simpler solution and if there's is a more efficent way of getting live notifications. Would a simple SetInterval() work well enough? Would calling the api on the server then emitting the data to the client with the web socket be more efficent? Thanks for any help/advice in advance.

Socket.io socket.broadcast.to not working

I have in my Express server
userbase = {'Fitz': '84Iw3MNEIMaed0GoAAAD'}; //Exapmle user to receive message and associated socket id
//Sending message to Example user
socket.on('send_msg',function(data_from_client){
//Testing for receivers socketId
console.log(userbase[data_from_client.to_user]);//This returns the socket id for Fitz in userbase successfully i.e 84Iw3MNEIMaed0GoAAAD
socket.broadcast.to(userbase[data_from_client.to_user]).emit('get_msg',{msg:data_server.msg});
});
Surprise surprise when I setup a handler for this event on my cliens side for 'get_msg' i get nothing.
.factory('SocketFctry',function(){
var socket = io('http://localhost:3002')
return socket;
})
.controller('ChatCtrl', function($scope,SocketFctry) {
SocketFctry.on('get_msg',function(received_message){
console.log(received_message);
$scope.$apply();
})
});
My other client side handlers are working fine.
SocketFctry.on('new_user', function(users,my_id){
console.log(users);
$scope.online_users = users;
$scope.$apply();
})
My version of socket.io is 1.3.7 .Am I missing something here?
socket.broadcast().to() send a message to all users that match the to() arguments EXCEPT the user who's socket it is. So, socket.broadcast.to(socket.id) will never send to the actual socket user. In fact, by default, it won't send to anyone.
Direct from the socket.io doc:
To broadcast, simply add a broadcast flag to emit and send method
calls. Broadcasting means sending a message to everyone else except
for the socket that starts it.
If you want to send to only a single socket, then just use:
socket.emit(...)
If you want to broadcast to a socket.id-type room and you want to include the user who's room it is, then use:
io.to('some room').emit('some event'):
So, you can change this:
socket.broadcast.to(userbase[data_from_client.to_user]).emit('get_msg',{msg:data_server.msg});
to this:
io.to(userbase[data_from_client.to_user]).emit('get_msg',{msg:data_server.msg});
Do you have users join a namespace on connection?
socket.join(userbase[data_from_client.to_user]); //'84Iw3MNEIMaed0GoAAAD'
You can try:
socket.in(userbase[data_from_client.to_user]).emit('get_msg', {msg:data_server.msg});

Not getting a notification when trying to use real-time database communications

I'm trying to use web sockets to add a new notification to my app. I'm using Backand as my server, and can't seem to get a grasp on responding to the new event. Here's my server side code to send the event:
//Action: Update - After data saved and committed
function backandCallback(userInput, dbRow, parameters, userProfile) {
//Send to array of users
socket.emitUsers("new_notification",userInput, ["admins"]);
return {};
}
And my client code to receive it:
//Wait for server updates on 'items' object
Backand.on('new_notification', function (data) {
//Get the 'items' object that have changed
console.log(data);
});
But when I try to receive the event in my app code, I never see a notification of type "new_notification". Can someone please let me know what's going on?
Make sure you are using emitRole instead of emitUsers. The code above emits to a user array, but it looks like you want to send a notification to the "admins" role. Change this to emitRole() and you should be ok.
For role you want to keep it case sensitive so it will be:
socket.emitRole("items_updated",userInput, "Admin");
For users the syntax is:
socket.emitUsers("items_updated",userInput, ["user2#gmail.com","user1#gmail.com"]);

Resources