stomp-js / stompjs

Javascript and Typescript Stomp client for Web browsers and node.js apps
Apache License 2.0
773 stars 81 forks source link

Recieving multiple notification for a single message #421

Closed DIVANSHMAHAJAN closed 2 years ago

DIVANSHMAHAJAN commented 2 years ago

I am using Stomp for a chat-application. However I end up getting multiple notifications for the same message sent to a user . The no of notifications is equal to the no of times the error occurs ERROR: websocket.js:6 WebSocket connection to (url) failed: Plz tell how to rectify this error. I believe that old connection persist and new connection is also made resulting in this error. <<< MESSAGE destination:/user/lakshaygrover7@gmail.com/queue/messages content-type:application/json subscription:sub-0 message-id:i0v1jswi-10 content-length:94

{"id":"61e9c1fb3eab087e3fd56371","senderId":"divanshmahajan09@gmail.com","senderName":"mmdmd"}

<<< MESSAGE destination:/user/lakshaygrover7@gmail.com/queue/messages content-type:application/json subscription:sub-0 message-id:gss3i31r-9 content-length:94

{"id":"61e9c1fb3eab087e3fd56371","senderId":"divanshmahajan09@gmail.com","senderName":"mmdmd"}

Id is exactly same resulting in 2 notification for 1 single message sent..

Polve commented 2 years ago

The message-id is different: are you sure you didn't subscribe twice for some reason?

DIVANSHMAHAJAN commented 2 years ago

No I don't think so I subscribed twice for any reason. Basically this is the code I am using for the chatcontent component. However when I render a diff component and then again come back to chatcontent component then this thing occurs. Everytime I render that component and come back to this component the no of notifications inc by 1 and the error that websokcet connection failed also inc by 1 in the console. PLZ HELP ME TO RESOLVE THIS ERROR.

chatcontent.js

import React, { useState, useEffect,useContext,useRef} from "react";
import { useRecoilValue, useRecoilState } from "recoil";
import {
  chatActiveContact,
  chatMessages,
  loggedInUser,
} from "../../atom/globalState";
 import { ToastContainer, toast } from 'react-toastify';

  import 'react-toastify/dist/ReactToastify.css';
import classes from "./chatContent.module.css";
import Avatar from "../ChatPage/Avatar";
import AuthContext from '../store/auth-context'
import ChatItem from "./ChatItem";
import axios from "axios";
import { message } from "antd";
var stompClient = null;
var counter=0;
const ChatContent = (props) => {
   const messagesEndRef = useRef(null);
  const authCtx = useContext(AuthContext);
  const currentUser = useRecoilValue(loggedInUser);
  const[deletemsgid,setdeletemsgid]=useState(-1);
  const [messagestate,setmessagestate]=useState('');
  const [messages, setMessages] = useRecoilState(chatMessages);
  const [activeContact, setActiveContact] = useRecoilState(chatActiveContact);
  const [contacts, setContacts] = useState([]);
  console.log("activecontact");
  console.log(activeContact.email);
  console.log("hsssssssssssssssssssssssssssss");
  console.log("currentUser");
  console.log(currentUser);
  useEffect(() => {

  }, [messages])

  useEffect(() => {
   if (localStorage.getItem("token") !== null) {
    connect();
  }
  }, [authCtx.isLoggedIn])
  useEffect(() => {
    if (localStorage.getItem("token") !== null&&props.index!=-1) {
   setActiveContact({name:props.nameofperson,email:authCtx.users[props.index].username})
  }
  }, [props.nameofperson,props.index])
  useEffect(() => {
    if (activeContact === undefined) {
      return;
    }
    const url =
      "https://chat-lg.azurewebsites.net/messages/" +
      activeContact.email +
      "/" +
      currentUser.username;
    axios
      .get(url, {
        headers: {
          Authorization: "Bearer" + authCtx.token,
        },
      })
      .then((response) => {
        //console.log(response.data);
        setMessages(response.data);
      });
  }, [activeContact.name,deletemsgid]);
   const scrollToBottom = () => {
   messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  };
  useEffect(() => {
    scrollToBottom()
  }, [messages]);
  const connect = () => {
    const Stomp = require("stompjs");
    var SockJS = require("sockjs-client");
    SockJS = new SockJS("https://chat-lg.azurewebsites.net/ws");
    stompClient = Stomp.over(SockJS);
    stompClient.connect({}, onConnected, onError);
  };

  const onConnected = () => {
    console.log("COONECTED HPGYE");
    counter++;
    console.log(counter);

    stompClient.subscribe(
      "/user/" + currentUser.username + "/queue/messages",
      onMessageReceived
    );
  };

  const onError = (err) => {
    window.alert("YES");
    console.log(err);
  };
  const changeinstate = (e) => {
    setmessagestate(e.target.value);
  };

  const onMessageReceived = (msg) => {
    console.log("STOMP");
    console.log(stompClient);
    const notification = JSON.parse(msg.body);
    const active = JSON.parse(sessionStorage.getItem("recoil-persist"))
      .chatActiveContact;
    console.log("active.enail");
    console.log(active.email);
    console.log(notification.senderId);
    if (active.email === notification.senderId) {
      const url =
        "https://chat-lg.azurewebsites.net/messages/" +
        notification.senderId +
        "/" +
        currentUser.username
        ;
      axios
        .get(url, {
          headers: {
            Authorization: "Bearer" + authCtx.token,
          },
        })
        .then((message) => {
          // const newMessages = JSON.parse(
          //   sessionStorage.getItem("recoil-persist")
          // ).chatMessages;

        setMessages(message.data);

          scrollToBottom();
        });
    } else {
      console.log("insiode of notification");
      console.log("current USERRRRRRRRRRRRRRRRRRR");
      console.log(notification.senderName);
      toast.info('Received a new message from ' + notification.senderName, {
position: "top-center",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});

    }
  };

  const sendMessage = () => {
    if (messagestate.trim() !== "") {
      // console.log("id");
      // console.log(currentUser._id);
      const message = {
        senderId: currentUser.username,
        recipientId: activeContact.email,
        senderName: currentUser.firstName,
        recipientName: activeContact.name,
        content: messagestate,
        timestamp: new Date(),
      };
      stompClient.send("/app/chat", {}, JSON.stringify(message));

      const newMessages = [...messages];
      newMessages.push(message);
      setMessages(newMessages);
      scrollToBottom();
      setmessagestate('');
    }
  };
  const logouthandler=()=>{
    authCtx.logout();
  }
  useEffect(()=>{
    if(deletemsgid!=-1)
    {
    }
  },[deletemsgid])
  return (
 <div className={classes.main__chatcontent}>
     <div className={classes.content__header}>
       <div className={classes.blocks}>
        <div className={classes['current-chatting-user']}>
          <Avatar
           isOnline="active"
           image="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTA78Na63ws7B7EAWYgTr9BxhX_Z8oLa1nvOA&usqp=CAU"
          />
           <p>{props.nameofperson}</p>

        </div>
       </div>

       <div className={classes.blocks}>
         <div className={classes.settings}>
          <button className={classes['btn-nobg']} onClick={logouthandler}>
             LOGOUT
           </button>

         </div>
       </div>
     </div>
     <div className={classes.content__body}>
       <div className={classes.chat__items}>
         {messages.map((itm, index) => {

           return (
          <ChatItem
          key={index}
         animationDelay={index + 2}
               id={itm.id}
               user={itm.type ? itm.type : "me"}
               msg={itm.content}
               timestamp={itm.timestamp}
               sender={itm.senderId}
               currentUser={currentUser.username}
               setdeletemsgid={setdeletemsgid}
               deletemsgid={deletemsgid}
            />

          );
         })}

         <div ref={messagesEndRef} />
       </div>
     </div>
     <div className={classes.content__footer}  >
       <div className={classes.sendNewMessage}>
         <button className={classes.addFiles}>
           <i className="fa fa-plus"></i>
         </button>
         <input
           type="text"
          placeholder="Type a message here"
           onChange={changeinstate}
           value={messagestate}
        />
        <button className={classes.btnSendMsg} id="sendMsgBtn" onClick={sendMessage}>
           <i className="fa fa-paper-plane"></i>
         </button>
       </div>
     </div>
   </div>
   );
 };

export default ChatContent;
kum-deepak commented 2 years ago

The issue, most likely, is caused by calling connect or subscribe twice. Please check the entire console log.

In case you would like me to have a look, you would need to upgrade to the latest version. The version you are using is from 2013 and is no longer supported. Please check the guides at https://stomp-js.github.io/. Also, check the samples at https://github.com/stomp-js/samples/. There are two chat versions using different variants of this library.

DIVANSHMAHAJAN commented 2 years ago

How to use stomp with react? I was using react-stomp for this purpose....Is it right? Also I wanted to ask that when does the connection get over....If we unmount the component from dom in which the stomp sockjs-client are there then will the connection break on it's own in react...? In my case I think the connection is not breaking when I move to some other component and the component in which stomp and sockjs-client code is written doesn't render and when I again go to that component another connection gets established due to the use of useeffect.....as shown above in the code..... Do we get any message in the console once the connection is over as shown in this https://github.com/lahsivjar/react-stomp/issues/163

kum-deepak commented 2 years ago

I know React; however, I do not program in React. I, however, program in Angular.

The stompjs interface was designed for usage from Javascript on a webpage. However, this interface is not suitable for Single Page Application (SPA) frameworks like Angular/React. So, I have written rx-stomp, a wrapper over stompjs that exposes a very different interface. To use rx-stomp, you need a basic understanding of rxjs.

Even though currently not evident, you will face challenges when you need to handle an error that causes the broker connection to drop, and you need to reconnect.

I will not be able to provide a copy-and-paste solution. If, however, you are committed to solving your problem, I can guide you to a solution.

DIVANSHMAHAJAN commented 2 years ago

THIS IS THE CODE THAT USES STOMP AND STOMPJS-CLIENT commands.. just these few lines enabled making a connection sending messages and all .. it is just the multiple connections that I am not able to understand.. when this component is used again...after closing...

PACKAGE.JSON FILE HAS "react-stomp": "^5.1.0", only...related to stomp or sock or anything else.. PACKAGE.LOCK.JSON: node_modules/react-stomp": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/react-stomp/-/react-stomp-5.1.0.tgz", "integrity": "sha512-LYgNpKITRL4BDDoVd4llC0t0CKsIipiSRV5SkDW7yI5wxlbuHcEi512dubNGtNLaaujNvgKAuFK9r8w7tecS4Q==", "dependencies": { "handlebars": "^4.7.6", "net": "^1.0.2", "sockjs-client": "^1.5.0", "stompjs": "^2.3.3" } } node_modules/stompjs": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/stompjs/-/stompjs-2.3.3.tgz", "integrity": "sha1-NBeKx7uO4pTMXVVK2LUPf1RZ/Y4=", "optionalDependencies": { "websocket": "latest" } }

REACT CODE: import React, { useState, useEffect,useContext,useRef} from "react"; import AuthContext from '../store/auth-context' var stompClient=null;

const ChatContent = (props) => { const authCtx = useContext(AuthContext); useEffect(() => { return () => { stompClient.forceDisconnect(); stompClient.deactivate(); }; }, [])

useEffect(() => { if (localStorage.getItem("token") !== null) { connect(); } }, [authCtx.isLoggedIn])

const connect = () => { const Stomp = require("stompjs"); var SockJS = require("sockjs-client"); SockJS = new SockJS("https://chat-lg.azurewebsites.net/ws"); stompClient=Stomp.over(SockJS); stompClient.connect({}, onConnected, onError); };

const onConnected = () => { stompClient.subscribe( "/user/" + currentUser.username + "/queue/messages", onMessageReceived ); };

const onError = (err) => { window.alert("YES");

};