tsm
tsm

Reputation: 509

How to stop socketio from reconnecting on every page refresh

I am trying to code the private messaging socketio example from: https://github.com/socketio/socket.io/tree/master/examples/private-messaging in React. However, I think I am initializing the connection incorrectly because every time the page refreshes, it creates a new connection. I don't really know where to go from here. Backend is the same as the repo above and this is the Chat component.

import React, { useEffect, useRef, useState } from "react";
import User from "./User";
import MessagePanel from "./MessagePanel";
import { io } from "socket.io-client";

import socketIo from '@/utils/socket'

function useSocket(url, username) {
  const [socket, setSocket] = useState(null)

  useEffect(() => {
    const socketIo = io(url, { auth:{username}, autoConnect: true });
    setSocket(socketIo)

  }, [])

  return socket
}

export default function Chat(props) {
  const [selectedUser, setSelectedUser] = useState(null)
  const [users, setUsers] = useState([])
  const API_URL='http://localhost:5000'
  const socket = useSocket(API_URL, props.name)

  useEffect(() => {
    const handleConnect = () => {
      console.log("A user has connected")
      const tempUsers = [...users]
      tempUsers.forEach((user) => {
        if (user.self) {
          user.connected = true
        }
      });
      setUsers(tempUsers)
    }

    if (socket) {
      const sessionID = localStorage.getItem("sessionID");

      if (sessionID) {
        socket.auth =  {...socket.auth, sessionID };
        // socket.connect();
      }

      socket.on("session", ({ sessionID, userID }) => {
        console.log("reconnection attempted")
        // attach the session ID to the next reconnection attempts
        socket.auth = {...socket.auth, sessionID };
        // store it in the localStorage
        localStorage.setItem("sessionID", sessionID);
        // save the ID of the user
        socket.userID = userID;
        
      });

      socket.on("connect", handleConnect)

      socket.on("disconnect", () => {
        const tempUsers = [...users]
        tempUsers.forEach((user) => {
          if (user.self) {
            user.connected = false
          }
        });
        console.log("tempUsers after disconnect" + tempUsers)
        setUsers(tempUsers)
      });

      socket.on("users", (usrs) => {
        const tempUsers = [...users]
        usrs.forEach((user) => {
          for (let i = 0; i <= tempUsers.length; i++) {
            const existingUser = tempUsers[i];
            if (existingUser && existingUser.userID === user.userID) {
              existingUser.connected = user.connected;
              setUsers(tempUsers)
              return;
            }
          }
          user.self = user.userID === socket.userID;
          initReactiveProperties(user);
          tempUsers.push(user);
        });
        tempUsers.sort((a, b) => {
          if (a.self) return -1;
          if (b.self) return 1;
          if (a.username < b.username) return -1;
          return a.username > b.username ? 1 : 0;
        });
        setUsers(tempUsers)
        // put the current user first, and sort by username
      });

    socket.on("user connected", (user) => {
      const tempUsers = [...users]
      for (let i = 0; i < tempUsers.length; i++) {
        const existingUser = tempUsers[i];
        if (existingUser.userID === user.userID) {
          existingUser.connected = true;
          setUsers(tempUsers)
          return;
        }
      }
      initReactiveProperties(user);
      tempUsers.push(user)
      console.log("tempUsers:" +  JSON.stringify(tempUsers))
      setUsers(tempUsers)
    });


    socket.on("user disconnected", (id) => {
      const tempUsers = [...users]
      for (let i = 0; i < tempUsers.length; i++) {
        const tempUser = tempUsers[i];
        if (tempUser.userID === id) {
          tempUser.connected = false;
          break;
        }
      }
      setUsers(tempUsers)
    });

    socket.on("private message", ({ content, from, to }) => {
      const tempUsers = [...users]
      for (let i = 0; i < tempUsers.length; i++) {

        const fromSelf = socket.userID === from;
        if (tempUsers[i].userID === (fromSelf ? to : from)) {
          tempUsers[i].messages.push({
            content,
            fromSelf,
          });
          if (tempUsers[i] !== selectedUser) {
            tempUsers[i].hasNewMessages = true;
          }
          break;
        }
      }
      setUsers(tempUsers)
    });
  }
  function cleanup() {
    socketIo.off("connect_error");
    socketIo.off("connect");
    socketIo.off("disconnect");
    socketIo.off("users");
    socketIo.off("user connected");
    socketIo.off("user disconnected");
    socketIo.off("private message");

}

return cleanup
  }, [socket])

  
    const initReactiveProperties = (user) => {
      user.messages = [];
      user.hasNewMessages = false;
    };

    function usernameInsideList(username, list) {
      for (let i = 0; i < list.length; i++) {
          if (list[i].username === username) {
              return true;
          }
      }
      return false;
    }

    function onSelectUser(user, idx) {
      console.log("User was selected")
        setSelectedUser(user);
        // user.hasNewMessages = false
        // selectedUser.current = user
        if (user.hasNewMessages) {
          let newArr = [...users];
          newArr[idx].hasNewMessages = false
          setUsers(newArr)
        }
    }

    function onMessage(content) {
      if (selectedUser) {
        socket.emit("private message", {
          content,
          to: selectedUser.userID,
        });
        const tempUser = {...selectedUser};
        tempUser.messages.push({
          content,
          fromSelf: true,
        });
        setSelectedUser(tempUser)
      }
    }

    function RightPanel() {
      if (selectedUser) {
        return ( 
        <MessagePanel
        user={selectedUser}
        onMessage={onMessage}
      />);
      } else {
        return (
          "Hello"
        )
      }
    }

    return (
      <div>
        <div className="left-panel">
          {users.map((user, id) => {
            return <User key={id} handleClick={onSelectUser} user={user}></User>;
          })}
        </div>
        <div className="right-panel">
          <RightPanel/>
        </div>
      </div>
    );
  }

Edit: I fixed the original issue by making the following edit. However, the code certainly isn't working as expected.


import React, { useEffect, useRef, useState } from "react";
import User from "./User";
import MessagePanel from "./MessagePanel";
import { io } from "socket.io-client";

export default function Chat(props) {

  const [selectedUser, setSelectedUser] = useState(null)
  const username = props.name;
  const [users, setUsers] = useState([])
  const API_URL='http://localhost:5000'
  const socket = useRef();

  useEffect(() => {
    socket.current = io(API_URL, { auth:{username}, autoConnect: true });
    
    const handleConnect = () => {
      const tempUsers = [...users]
      tempUsers.forEach((user) => {
        if (user.self) {
          user.connected = true
        }
      });
      setUsers(tempUsers)
    }

    if (socket.current) {
      const sessionID = localStorage.getItem("sessionID");

      if (sessionID) {
        socket.current.auth =  {...socket.current.auth, sessionID };
        // socket.connect();
      }

      socket.current.on("session", ({ sessionID, userID }) => {
        console.log("reconnection attempted")
        // attach the session ID to the next reconnection attempts
        socket.current.auth = {...socket.current.auth, sessionID };
        // store it in the localStorage
        localStorage.setItem("sessionID", sessionID);
        // save the ID of the user
        socket.current.userID = userID;
        
      });
      socket.current.on("connect", handleConnect)

      socket.current.on("disconnect", () => {
        const tempUsers = [...users]
        tempUsers.forEach((user) => {
          if (user.self) {
            user.connected = false
          }
        });
        console.log("tempUsers after disconnect" + tempUsers)
        setUsers(tempUsers)
      });

      socket.current.on("users", (usrs) => {
        const tempUsers = [...users]
        usrs.forEach((user) => {
          for (let i = 0; i <= tempUsers.length; i++) {
            const existingUser = tempUsers[i];
            if (existingUser && existingUser.userID === user.userID) {
              existingUser.connected = user.connected;
              setUsers(tempUsers)
              return;
            }
          }
          user.self = user.userID === socket.userID;
          initReactiveProperties(user);
          tempUsers.push(user);
        });
        setUsers(tempUsers)
        // put the current user first, and sort by username
      });

    socket.current.on("user connected", (user) => {
      const tempUsers = [...users]
      for (let i = 0; i < tempUsers.length; i++) {
        const existingUser = tempUsers[i];
        if (existingUser.userID === user.userID) {
          existingUser.connected = true;
          setUsers(tempUsers)
          return;
        }
      }
      initReactiveProperties(user);
      tempUsers.push(user)
      setUsers(tempUsers)
    });


    socket.current.on("user disconnected", (id) => {
      const tempUsers = [...users]
      for (let i = 0; i < tempUsers.length; i++) {
        const tempUser = tempUsers[i];
        if (tempUser.userID === id) {
          tempUser.connected = false;
          
          break;
        }
      }
      setUsers(tempUsers)
    });

    socket.current.on("private message", ({ content, from, to }) => {
      console.log("private message")
      const tempUsers = [...users]
      for (let i = 0; i < tempUsers.length; i++) {

        const fromSelf = socket.userID === from;
        if (tempUsers[i].userID === (fromSelf ? to : from)) {
          tempUsers[i].messages.push({
            content,
            fromSelf,
          });
          console.log("tempUser and selectedUser" + tempUser[i] + selectedUser)
          if (tempUsers[i] !== selectedUser) {
            tempUsers[i].hasNewMessages = true;
          }
          break;
        }
      }
      setUsers(tempUsers)
    });
  }
    function cleanup() {
      socket.current.off("connect_error");
      socket.current.off("connect");
      socket.current.off("disconnect");
      socket.current.off("users");
      socket.current.off("user connected");
      socket.current.off("user disconnected");
      socket.current.off("private message");
    }
    return cleanup
  }, [])

    //Use this to decrease DRY
    // const updateState = (state, setState, ...args) => {
    //   console.log(args)
    //   let newArr = [...state];
    //   newArr[idx].connected = true
    //   setState(newArr)
    // }

    const initReactiveProperties = (user) => {
      user.messages = [];
      user.hasNewMessages = false;
    };
    
    function usernameInsideList(username, list) {
      for (let i = 0; i < list.length; i++) {
          if (list[i].username === username) {
              return true;
          }
      }
      return false;
    }

    function onSelectUser(user, idx) {
        setSelectedUser(user);
        // selectedUser.current = user
        if (user.hasNewMessages) {
          let newArr = [...users];
          newArr[idx].hasNewMessages = false
          setUsers(newArr)
        }
    }
    function onMessage(content) {
      if (selectedUser) {
        socket.current.emit("private message", {
          content,
          to: selectedUser.userID,
        });
        const tempUser = {...selectedUser};

        tempUser.messages.push({
          content,
          fromSelf: true,
        });
        setSelectedUser(tempUser)
      }
    }
    function RightPanel() {
      if (selectedUser) {
        return ( 
        <MessagePanel
        user={selectedUser}
        onMessage={onMessage}
      />);
      } else {
        return (
          "Hello"
        )
      }
    }

    return (
      
      <div>

        <div className="left-panel">

          {users.map((user, id) => {
            return <User key={id} idx={id} handleClick={onSelectUser} user={user}></User>;
          })}
        </div>

        <div className="right-panel">
          <RightPanel/>
        </div>
      </div>
    );
  }

Here is the vue component I am trying to recreate.

<template>
  <div>
    <div class="left-panel">
      <user
        v-for="user in users"
        :key="user.userID"
        :user="user"
        :selected="selectedUser === user"
        @select="onSelectUser(user)"
      />
    </div>
    <message-panel
      v-if="selectedUser"
      :user="selectedUser"
      @input="onMessage"
      class="right-panel"
    />
  </div>
</template>

<script>
import socket from "../socket";
import User from "./User";
import MessagePanel from "./MessagePanel";

export default {
  name: "Chat",
  components: { User, MessagePanel },
  data() {
    return {
      selectedUser: null,
      users: [],
    };
  },
  methods: {
    onMessage(content) {
      if (this.selectedUser) {
        socket.emit("private message", {
          content,
          to: this.selectedUser.userID,
        });
        this.selectedUser.messages.push({
          content,
          fromSelf: true,
        });
      }
    },
    onSelectUser(user) {
      this.selectedUser = user;
      user.hasNewMessages = false;
    },
  },
  created() {
    socket.on("connect", () => {
      this.users.forEach((user) => {
        if (user.self) {
          user.connected = true;
        }
      });
    });

    socket.on("disconnect", () => {
      this.users.forEach((user) => {
        if (user.self) {
          user.connected = false;
        }
      });
    });

    const initReactiveProperties = (user) => {
      user.messages = [];
      user.hasNewMessages = false;
    };

    socket.on("users", (users) => {
      users.forEach((user) => {
        for (let i = 0; i < this.users.length; i++) {
          const existingUser = this.users[i];
          if (existingUser.userID === user.userID) {
            existingUser.connected = user.connected;
            return;
          }
        }
        user.self = user.userID === socket.userID;
        initReactiveProperties(user);
        this.users.push(user);
      });
      // put the current user first, and sort by username
      this.users.sort((a, b) => {
        if (a.self) return -1;
        if (b.self) return 1;
        if (a.username < b.username) return -1;
        return a.username > b.username ? 1 : 0;
      });
    });

    socket.on("user connected", (user) => {
      for (let i = 0; i < this.users.length; i++) {
        const existingUser = this.users[i];
        if (existingUser.userID === user.userID) {
          existingUser.connected = true;
          return;
        }
      }
      initReactiveProperties(user);
      this.users.push(user);
    });

    socket.on("user disconnected", (id) => {
      for (let i = 0; i < this.users.length; i++) {
        const user = this.users[i];
        if (user.userID === id) {
          user.connected = false;
          break;
        }
      }
    });

    socket.on("private message", ({ content, from, to }) => {
      for (let i = 0; i < this.users.length; i++) {
        const user = this.users[i];
        const fromSelf = socket.userID === from;
        if (user.userID === (fromSelf ? to : from)) {
          user.messages.push({
            content,
            fromSelf,
          });
          if (user !== this.selectedUser) {
            user.hasNewMessages = true;
          }
          break;
        }
      }
    });
  },
  destroyed() {
    // socket.off("connect");
    // socket.off("disconnect");
    // socket.off("users");
    // socket.off("user connected");
    // socket.off("user disconnected");
    // socket.off("private message");
  },
};
</script>

<style scoped>
.left-panel {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 260px;
  overflow-x: hidden;
  background-color: #3f0e40;
  color: white;
}

.right-panel {
  margin-left: 260px;
}
</style>

Upvotes: 0

Views: 964

Answers (1)

ragu karuturi
ragu karuturi

Reputation: 134

Socket connections are disconnected on page refresh and that is the expected behavior across browsers.

Upvotes: 1

Related Questions