Socket.io Client repeats event action multiple times from one emit - javascript

I am creating a simple diffie hellman demo with sockets and it seems that whenever my server emits any event, the client repeats it multiple times.
For example: On the client side whenever I add a message I emit this new message to the server like so.
const addMessage = (text) => {
var newMessage = {
id: nextID,
from: name,
message: text
}
socket.emit("send-message", newMessage);
setNextID(nextID + 1);
setMessages([...messages, newMessage]);
}
And then on the server side I add that message to the messages and emit the new bank of messages to the other clients.
// Send message to other person
socket.on("send-message", (message) => {
messages = [...messages, message];
console.log(messages, "Newline\n");
socket.broadcast.emit("receive-message", messages);
})
The clients receive it and updates their message bank. I console.log these actions.
socket.on("receive-message", (receivedMessages) => {
console.log("receieved messages");
setMessages(receivedMessages);
});
But when I send a message it console.logs it repeats it like 100 times??
This happens for a lot of my other client side events handlers, repeating a action more than once, from just one emit from the server.
Why does this happen and how can I stop it?

If you are using ReactJs, be aware that if you add the listener in your components, any time the component gets updated it will add a new socket listener and for example on second second render, you'll get 2 console.log s. So you must add the listener in your useEffect hook (componentDidMount in class component case).
And consider that even your components can be viewed a few times so then the listener will be duplicated. So you have to close your listeners on useEffect's return (componentWillUnmount in class component case)
So it must be like:
useEffect(() => {
socket.on("receive-message", (receivedMessages) => {
console.log("receieved messages");
setMessages(receivedMessages);
});
return () => {
socket.off("receive-message");
};
}, []);

try to change like:
client-side:
socket.emit(“your event name”, “msg”)
server-side:
var io = new require(“socket-io”)()
io.on(“connection”, (socket) => {
socket.on(“your event name”, (msg) => {
io.emit(“your event name”, msg)
})
})
try it out and it will be better :D

Just send the new message
Client
socket.on("receive-message", (receivedMessages) => {
console.log("receieved messages");
setMessages(prevMessage => [...prevMessage, receivedMessages]);
});
Server
socket.on("send-message", message => {
messages = [...messages, message];
console.log(messages, "Newline\n");
socket.broadcast.emit("receive-message", message);
})

Related

Zendesk - Listen agent response event ZAF Client [Support Location]

for a while now I am stuck, trying to listen to the event where the agent sends a reply to the ticket. I have tried listening to ticket.comments.changed and ticket.conversation.changed but have not been successful.
I can't use the ticket.submit.[done|fail|always] or ticket.save because I don't have a way of knowing if it is the event I want or is being called with another event.
Maybe someone who knows of a configuration or some way that would allow me to do this, I would be very grateful.
You can configure triggers and webhooks to be added to your app as requirements. If you must use a ticket_sidebar app, you can listen to the following events:
var client = ZAFClient.init();
client.on('ticket.comments.changed', (e) => {
// Here is the latest comment
let comment = e[0];
console.log(comment);
// Check author role
console.log((comment.author.role !== "end-user") ? "Comment made by agent" : "Comment made by end user");
// Get ticket object if needed
client.get('ticket').then(
(res) => {
// Send ticket payload to my backend
},
(err) => {
console.error(err);
}
)
});
client.on('ticket.status.changed', (e) => {
// Here is the new status
console.log("Status changed to", e);
// Get ticket object if needed
client.get('ticket').then(
(res) => {
// Send ticket payload to my backend
},
(err) => {
console.error(err);
}
)
});

RxJS: unsubscribe from nested subscribe

I use SockJS and StompJS and when I open my application in the browser sometimes it tries to subscribe to some topics before it even connected to the websocket. I'd like the topic subscription wait until the app is connected to websocket.
export class SocksService {
...
public subscribe<T>(destination: string, callback?: (body: T) => void, headers?: object): Observable<Subscription> {
const subscription = new Subject<Subscription>();
headers = headers || {};
this.status.pipe(
first(status => status === ConnectionStatus.CONNECTED)
).subscribe((status: ConnectionStatus) => {
subscription.next(this.stompClient.subscribe(destination, (message: Message) => {
if (callback) {
callback(JSON.parse(message.body) as T);
}
}, headers));
});
return subscription.asObservable();
}
...
}
That's why I implemented this code and I call it like:
this.socksService.subscribe<User>('/topic/user', (user: User) => {
console.log('user received', user);
}).subscribe(subscription => this.userSubscription = subscription);
So I subscribe to the topic only when the connection status is connected and it will be called only for the first time the client successfully connects.
I'd like to unsubscribe later from the topic, so I need the Subscription object returned by the inner subscribe and I also need the message from the inner subscribe.
What I implemented works good, but I think there must be a better way to do it.
(I tried rx-stomp, but it has many bugs.)
You said you tried rx-stomp but have you tried the Angular2+ version of rx-stomp, called ng2-stompjs? It exposes key classes as Angular Injectable Services.
See the mention of it under rx-stomp, here:
https://github.com/stomp-js/ng2-stompjs#documentation
...and it's implementation (Angular 7) via a nice step-by-step, here:
https://stomp-js.github.io/guide/ng2-stompjs/ng2-stomp-with-angular7.html#receiving-messages
e.g.:
this.rxStompService.watch('/topic/demo').subscribe((message: Message) => {
this.receivedMessages.push(message.body);
});

React + Socket IO: Accessing the first message

I'm trying to create an auto reply after a user has sent his first message. I have no problems with the reply, it's just knowing when the a user has sent his first message (and first message only).
I have an USER prop that contains users id.
here is my hook that is responsible for the message sending
useEffect(() => {
if (socket) {
socket.on("msg", msg=> {
dispatch({
type: ACTIONS.MSG,
value: msg
});
});
in the reducer the messages is an array. So I've been thinking of just creating something like
PSEUDO CODE if (the user PROP.Type === 'userType1' && sends.message[0]) {
return autoReply
}

socket.io - stop receiving own emits

TL;DR - How to prevent client from receiving its own messages?
So I'm playing with socket.io after my experience with apollo and graphql.
My simple server looks like this:
io.on('connection', (socket) => {
console.log('New connection established.');
socket.on('disconnect', () => {
console.log('User disconnected.');
});
// Projects:
socket.on('join project', (data) => {
console.log(`User (${data.user.email}) join project with ID ${data.project.id}`);
socket.join(data.project.id);
});
socket.on('leave project', (data) => {
socket.leave(data.project.id);
});
socket.on('change field', (data) => {
console.log('Field was changed:', data);
const { project } = data;
socket.to(project.id).broadcast.emit('field changed', data);
});
});
I'm emitting something like this inside my application:
socket.emit('change field', {
project: {
id: 1,
},
value: 'Hello world',
usersEmail: 'example#email.com',
fieldName: 'description',
});
socket.on('field changed', (data) => {
// if (data.usersEmail === 'example#email.com') return; // This would stop from receiving own messages
console.log('CLIENT: field was changed!', data);
});
What I thought would happen is (due to the broadcast flag that I set up in the on('change field', ...)):
Clients A emits the message
Clients other than A receive the message
What is happening is a log inside other clients and client A itself, saying that the field was changed. Am I missing something?
I had the exact same problem. Couldn't (or didn't try hard enough) to find a setting for it, so instead just added this to my clients on page load:
document.windowid = Math.round(Math.random() * 1000000000000);
Then, add this to the message you emit in your client:
windowid: document.windowid
Then, when you accept data on the client, only do the action when windowid is not the same:
if (message.windowid != document.windowid)
It's not great and socket.io should take care of this issue, but this is the solution I used in my app :)

Issue with socket.io updating component to show new message in chatroom

I am building a chat app with React, Node/Express and socket.io. I have my sockets successfully set to my express server via http.createServer. I have a listener on client and server listening for new messages coming into the chat room. Ideally, I want each instance of the chat to be updated when there is an additional message, like any chat room that ever existed :)
Now I have a successful listen between client and server. I know because of a console.log server-side. However, I am not re-rendering the chat component when I submit a new message from a different instance.
So my code in my client-side (again React) component is as follows and I am using the socket CDN with script tags in my index.html (script tags not shown):
Socket CDN here
var socket = io('')
So that is the socket you see client side :
componentDidMount() {
return axios.get(`api/messages`)
.then((result) => {
if (result.data.length) {
this.setState({
messages: [ ...this.state.messages, ...result.data]
} , () => {
console.log("The state after messages are mounted : ", this.state)
})
}
})
.catch((err) => { throw err})
socket.on('new message', msg => {
this.newMessage(msg);
})
};
newMessage(msg) {
this.setState({
messages: [...this.state.messages, msg]
}, () => {
this.setState({ message: '' })
return this.scrollToBottom()
});
};
onSubmitMessage(event) {
event.preventDefault();
const content = this.state.message;
const msg = {
content,
createdAt : new Date(),
userId : "one",
chatRoomId : "two"
}
axios.post(`api/messages/`, msg)
.then(() => {
this.newMessage(msg);
socket.emit('new message', msg); //HERE'S THE SOCKETS IN ACTION
})
};
Here is the server-side code Node/Express:
//in server.js
const io = new socketIo(server)
require('./socketEvents')(io);
const connections = [];
Then a separate file for my socket events
//in socketEvents.js
module.exports = (io) => {
io.on('connection', (socket) => {
console.log("Beautiful sockets are connected")
socket.once('disconnect', () => {
console.log("socket is disconnected");
});
//DOESN'T DO ANYTHING YET
socket.on('join global', (username) => {
socket.join(username);
console.log("New user in the global chat : ", username)
});
socket.on('new message', (msg) => {
console.log("The new message from sockets : ", msg);
socket.emit('new message', msg.content);
});
});
}
My sockets server side are linked up with the client. I'm just not seeing new messages in different instances. Is it because I'm not re-rendering after the server receives the message?
Thanks in advance, please let me know if you need me to clarify anything.
Cheers!
I figured it out... I'm going to leave this post up with a walkthrough in an attempt to help others who are having trouble with sockets. I may post a blog about it. Will update if I do.
So the code listens on the client side for a message to be sent inside of my onSubmitMessage function.
onSubmitMessage(event) {
event.preventDefault(); //prevents HTML <form> from going on its own post
const content = this.state.message;
//Create message object
const msg = {
content,
createdAt : new Date(),
userId : "one",
chatRoomId : "two"
}
//HERE'S THE IMPORTANT PART!!!
axios.post(`api/messages/`, msg)
.then(() => {
// wrapped in a promise, send a handler to server called
// ('new message') with the message object
this.newMessage(msg);
socket.emit('new message', msg);
})
.then(() => {
//Another promise then waits for the handler to come back from server
//*****IMPORTANT*************
//Then invoke newMessage function to get the post on all sockets
socket.on('message', (msg) => {
this.newMessage(msg);
})
})
};
Now on the server side this is what's happening:
// This is where the listener is for the client side handle
socket.on('new message', (msg) => {
// broadcast.emit will send the msg object back to client side and
// post to every instance expcept for the creator of the message
socket.broadcast.emit('message', msg);
});
SO the data path is (C) for client, (S) for server:
receive message object from user and -------->
(C)socket.emit('new message') -----> (S) socket.on('new message') -------> (S) socket.broadcast.emit('message') --------> (C)socket.on('message')
Back in the client side, I can invoke my newMessage function, which will set the message to state so I can display it.
I hope someone finds this useful! Surprisingly, this seems to go relatively unanswered on Stack. If anyone has any questions feel free to ask!

Categories