WebSocket readyState stuck at 0 after a couple of messages are sent

Viewed 462

I am trying to develop a live chat app using web sockets and react, but after I try submitting several messages (around 30) the web socket gets stuck on the CONNECTING state. I have it set up so when it does send a message it disabled the send button to prevent users from spamming messages too fast but I unfortunately still get the same issue.

// id is a uuid() string
const ws = new WebSocket(`ws://localhost:3001/chat/${id}`);

useEffect(() => {
        ws.onmessage = function(evt){
            try{
                const user_id = parseInt(evt.data.split("")[0]);
                const message = evt.data.slice(1);
                const currentTime = new Date();
                const currentUTC = currentTime.toUTCString();
                const timestamp = new Date(currentUTC);
                setMessages(messages => [...messages, {user_id, message, timestamp}])
            } catch(e){
                console.log(e);
            }
        }

        ws.onclose = function(evt){
            console.log("DISCONNECTED!!")
            ws.close();
        }

        ws.onerror = function(evt){
            console.log(evt);
            ws.close();
        }
    }, []);


useEffect(() => {
        async function postMessageToAPI() {
            
            const messsageToSend = {
                unique_id: id,
                message: formData.message, 
                user_id: user.id, 
                group_chat_id: room.id
            }
            // Convert to unviersal time UTC and send it to database
            let currentUTC = new Date();
            currentUTC.toUTCString();
            messsageToSend.timestamp = currentUTC;

            await AnonChatApi.sendChatMessage(messsageToSend);
        }

        if(sendMessage){
            ws.onopen = function(){
                // add user_id to the start of the message string
                const message = `${user.id}` + formData.message;
                ws.send(message);
            }
            postMessageToAPI();
            resetFormData();
            setTimeout(() => {
                setSendMessage(false)
            }, 1000);
        }
    }, [sendMessage]);

const goBackHome = () => {
        ws.close();
        history.push('/');
    }
1 Answers

I can see you're using Hooks, so you must also be using Function Components. Am I correct in thinking that the code to initialize the websocket

const ws = new WebSocket(`ws://localhost:3001/chat/${id}`);

is at the top of the function?

As a reminder, the function which defines your Function Component is run whenever your component is rendered. Anything that isn't saved in state is lost. This includes your websocket - a new one will be created every render, your async functions may sending data on an old websocket (from a previous render), and React may warn you in the console that you have a memory leak.

useEffect is the proper approach here, but the websocket also needs to be saved in state.

YourFunctionComponent() {
  const [ws, setWs] = useState(null);

  useEffect(() => {
    if (ws == null) {
      setWs(new WebSocket(`ws://localhost:3001/chat/${id}`));
    }
    return () => {
      // A function returned from useEffect will
      // get called on component unmount. 
      // Use this function to clean up your connection and 
      // close your websocket!

      // clean up, e.g.
      // ws.send('closing due to unmount!');
      
      ws.close();
      setWs(null);
    }
  }, [ws, setWs]);


  // Add `ws` as a dependency in the useEffect()s you posted above
  useEffect(() => {
    ws.onmessage = function(evt){
[--snip--]
    }
  }, [ws]);

  useEffect(() => {
    async function postMessageToAPI() {
[--snip--]
    }
  }, [sendMessage, ws]);
}
Related