Socket io multiple connections in react application

Viewed 1134

I decided to learn socket io and do a little project with it, so I was following the docs creating an instance and connecting to it. so on my server side I have:

onst express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server, {
  cors: {
    origin: "*",
  },
});

app.get("/home", (req, res) => {
  res.send("hello");
});

io.on("connect", (socket) => {
  console.log(socket.id);
  socket.on('disconnect',()=>{console.log("socket disconnected")
});

server.listen(5000, () => {
  console.log("listening");
});

and on my client side :

mport React, { useState, useEffect } from "react";
import io from "socket.io-client";
 
const ENDPOINT = "http://localhost:5000";

function App() {
  const [message, setMessage] = useState("");
  useEffect(()=>{
  const socket = io(ENDPOINT);
  return()=>{socket.emit('disconnect');
     socket.off();}},[endpoint]);
  return (
    <div className="App">
      <input
        type="text"
        placeholder="type a message"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
    </div>
  );
}

export default App;

However the connection is happening multiple times and many reconnects are happening in the same browser tab, I tried useEffect and it still didn't work once.

In the borwser when I open the dev tools to see the network i see continuously socket.io/?EIO=4&transport=polling. On my console I get the id of the socket then it disconnection message (I updated the code to show it) here is my index.js:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

1 Answers

You were right to use useEffect for this, but you also need to add the socket connection to the state. You should also pass a callback into your connect listener. Try this:

import {io} from "socket.io-client";

function App() {
  const [message, setMessage] = useState("");
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = io(ENDPOINT);
    newSocket.on('connect', (socket) => {
      console.log('socket connected', socket);
    })
    
    setSocket(newSocket);
    return () => newSocket.close();
  }, [setSocket]);

  return (
    <div className="App">
      <input
        type="text"
        placeholder="type a message"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
    </div>
  );
}

*Note adding the return () => newSocket.close(); in useEffect cleans up the connection by closing it (and sending the close event) when the user navigates away.

If this doesn't work you can always move the socket connection (and listeners) to outside of the component, like this:

import {io} from "socket.io-client";

const newSocket = io(ENDPOINT);

newSocket.on('connect', (socket) => {
   console.log('socket connected', socket);
})


function App() {
  const [message, setMessage] = useState("");

  return (
    <div className="App">
      <input
        type="text"
        placeholder="type a message"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
    </div>
  );
}

This might work better, in which case I would move this into its own file and handle all the event listeners in one place.

Related