3

Before this is flagged as a duplicate, I have checked the majority of other posts and solutions, to no avail. If anyone wants to double check, here they are:

1) Socket.io Multiple returns for a single event 2) Socket.io message event firing multiple times
3) socket.on event gets triggered multiple times

And many others.

Now to get to the meat of my question!

I have a socket in my client and my server code. Once my server-side socket emits a message, it is received by the client-side socket and prints out the message. With research, this probably ties into event listeners, but I can't find out how to apply it to my code. FYI the following client code is ran when a button is clicked.

Here are snippets of my client code

onButtonClick() {
     socket.emit('message_to_server', 'ping');

     socket.on('reply', (tmp) => {
          console.log(tmp); // in this case, call it 'pong'
          this.doSomethingWithMe(tmp);
     });
}

doSomethingWithMe(msg) {
// do something with the information
}

The first time I click the button, I receive

> pong

The second time I click the button, I receive

> pong
> pong

It continues to grow exponentially.

I can post my server code if needed, but I'm 100% sure that it emits the information correctly.

Does anyone have any idea for fixing this issue? I can't figure out how the listeners play into this scenario, so I would appreciate any advice!

EDIT: I changed some of my code to the following:

import React ...

const socket = socketIOClient('http://localhost:3000')

socket.on('reply', (tmp) => {
     console.log(tmp); // in this case, call it 'pong'
     var inst = new drawMe();
     inst.doSomethingWithMe(tmp);     
});

class drawMe extends Component {
     constructor(props) { this.state = { allData: ''}}

     onButtonClick() {
          ...
     }

     doSomethingWithMe(data) {
          this.setState({ allData: data });
     }

I am now receiving an error saying that you cannot call setState on a component that is not yet mounted. I probably will not open another question for this issue, but I would appreciate any advice on it. If mods/anyone wants me to close, I have no problem doing so.

EDIT2: If anyone else has this issue, I made it work by moving the code to instantiate the socket and for the event inside my constructor.

lve
  • 307
  • 2
  • 5
  • 15

1 Answers1

4

You're attaching your event handler again every time the button is clicked. You have socket.on inside the click handler; that method attaches a new handler -- that is, each time it's run, it adds the specified function to the end of the list of functions to run when the event fires. So yes, every time you click the button, you're adding the function to the end of the list and it'll run once for every time it was added. (More accurately, since you're using an anonymous function there, it's creating a new function every time you click the button and adding it to the event handler list to be run when the event fires.)

You should only be attaching event handlers once, for instance just after creating the socket, not on every click.

IceMetalPunk
  • 5,476
  • 3
  • 19
  • 26
  • Thanks for the very quick reply! So that does make sense, but to clarify: would I write `socket.on('reply', (tmp) => {...}` right below where I instantiate the socket itself (`const socket = socketIOClient('localhost')`? I'm – lve Jul 10 '19 at 17:27
  • Yes, that should work just fine :) As long as you only run the `socket.on` handler attachment code once, it'll work as you expect. (I also just noticed you're missing the closing `)` after your handler function definition, so I'm surprised the code didn't crash in the first place :P ) – IceMetalPunk Jul 10 '19 at 17:28
  • I'll definitely try it right now. In my current implementation, my server is emitting the 'pong' message _x_ (an unknown) amount of times. Do you think that the socket will read all of my server emits? – lve Jul 10 '19 at 17:30
  • An event handler is attached so that it will run *any time* the event fires. If the server triggers the event 23 times, the handler function will run 23 times, in exactly the order the events were received. – IceMetalPunk Jul 10 '19 at 17:32
  • So yeah it works! However, when I am calling `doSomethingWithMe(data)`, it is out of scope because my socket instantiation is completely above my class. I know that this isn't the original question, but do you have any ideas to fix this? – lve Jul 10 '19 at 17:35
  • I don't see any of your class definition in the code you provided. You should have an instance of your class somewhere; just call `doSomethingWithMe` on that instance rather than trying to access `this`, which doesn't point to the class outside of the class. – IceMetalPunk Jul 10 '19 at 17:41
  • Here, let me post some code in an edit in my question. It'll explain what I'm doing pretty quickly. I'm ending up with an error saying that my component is not mounted, but that might be a React error. – lve Jul 10 '19 at 17:43
  • You never mentioned you were using React before; that's a very important bit of information we needed to know, since frameworks tend to do a lot of stuff in the background that you need to work around/with. React components shouldn't be instantiated manually. What you should do instead is attach the event handler inside your component class, but do it when the component mounts, so it only happens once (per instance of the component) but you still have access to class methods (so you can use `this.doSomethingWithMe`). – IceMetalPunk Jul 10 '19 at 18:38