0

I have three rooms General, TypeScript and Nest. I'am joining General room in the first and second browser tab. When I decide to leave General from the second tab and join TypeScript room I still get the messages from General even though I should have left it. The bug occures when you switch to room that has been in use by other client and leave it. I'am using NodeJs, Socket.io, NestJS and React.

Server

@WebSocketGateway()
 export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
 
  @WebSocketServer() server: Server;
  private logger: Logger = new Logger('ChatGateway');
 
  @SubscribeMessage('msgToServer')
  handleMessage(client: Socket, message: { sender: string, room: string, message: string }): void {
    
   this.server.to(message.room).emit('msgToClient',  message);
   this.logger.log(`msgToClient: ${message}`);
   

   
  }
  @SubscribeMessage('joinRoom')
  handleRoomJoin(client: Socket, room: string ) {
    client.join(room);
    client.emit('joinedRoom', room);
    this.logger.log(`Client joined ${room}`);
  }

  afterInit(server: Server) {
   this.logger.log('Init');
  }
 
  handleDisconnect(client: Socket) {
   this.logger.log(`Client disconnected: ${client.id}`);
  }
 
  handleConnection(client: Socket, ...args: any[]) {
   this.logger.log(`Client connected: ${client.id}`);
  }
  @SubscribeMessage('leaveRoom')
  handleRoomLeave(client: Socket, room: string ) {
    console.log(client.id);
   /*  console.log(client.rooms);
    console.log(client.adapter.rooms); */
    client.leave(room);
  /*   console.log(room); */
  /*   client.rooms = {};
    delete client.adapter.rooms[room]; */
    client.emit('leftRoom', room);
    this.logger.log(`Client left ${room}`);
  /*   console.log(client.adapter.rooms);
    console.log(client.rooms); */
   // console.log(client.adapter.rooms) 
    
  }
 }

Client

  const [state, setState] = useState({ sender: "", room: "", message: "" });
  const [chat, setChat] = useState([
    { sender: "Peter", room: "General", message: "test" },
  ]);
  const socket = io(BASE_URL);
  const [room, setRoom] = useState({
    General: false,
    TypeScript: false,
    NestJS: false,
  });
  console.log(room);
  useEffect(() => {
    socket.on("msgToClient", (msg) => {
      setChat([...chat, { ...msg }]);
    });
  });

  const onTextChange = (e) => {
    setState({ ...state, [e.target.name]: e.target.value });
  };

  const onMessageSubmit = (e) => {
    e.preventDefault();
    const { sender, room, message } = state;
    socket.emit("msgToServer", { sender, room, message });
    setState({ message: "", sender: "", room: "" });
  };

  const toggleRoomMembership = (chatroom) => {
    const isMemberOfActiveRoom = (chatroom) => {
      return room[chatroom];
    };
    if (isMemberOfActiveRoom(chatroom)) {
      setRoom({ ...room, [chatroom]: false });
    
      socket.emit("leaveRoom", chatroom);
    } else {
      setRoom({ ...room, [chatroom]: true });

      socket.emit("joinRoom", chatroom);
    }
  };

  const renderChat = () => {
    return chat.map(({ sender, message }, index) => (
      <div key={index}>
        <h3>
          {sender}: <span>{message}</span>
        </h3>
      </div>
    ));
  };
retro
  • 51
  • 8

1 Answers1

0

Turns out the solution is simple. Put the socket into the useEffect and create state for it.

const [socket, setSocket] = useState({});
     useEffect(() => {
    const socket = io(BASE_URL);
    setSocket(socket);
    socket.on("msgToClient", (msg) => {
      setChat([...chat, { ...msg }]);
     
    });
    
  }, []);
retro
  • 51
  • 8