Darien Miller
Darien Miller

Reputation: 753

Adding values to stateful array using React WebSocket doesn't grow the array. Why does this happen?

I'm trying to incorporate WebSockets into a React chat application, and upon receiving a message from the backend and adding it the messages array, the array just overwrites the first value and stays at a size of one instead of incrementing. I don't seem to know why, as when the client sends a message, that message is appended to the array and it grows as expected, but when that same client receives a message, the array is cleared out and all the messages except for the one that came in is gone. How do I fix this?

import './App.css';
import { useState, useEffect } from "react"

const socket = new WebSocket("ws://localhost:8080/ws");

function App() {
    const [messages, setMessages] = useState([])
    const [inputValue, setInputValue] = useState("")

    useEffect(() => {
      socket.onopen = () => {
        console.log("connected")
      }

      socket.onmessage = msg => {
          const parsedMessage = JSON.parse(msg.data)

          //The array is never incremented when the client receives a message. Messages
          //added by the client through the input are erased when this function is invoked.
          setMessages([...messages, {className: "other-message", body: parsedMessage.body}])
      };

      socket.onclose = event => {
          console.log("Socket Closed Connection: ", event);
      };

      socket.onerror = error => {
          console.log("Socket Error: ", error);
      };

    }, [])

    const addMessage = () => {
      socket.send(inputValue);

      //Here, messages are appended to the array as expected.
      setMessages([...messages, {className: "message", body: inputValue}])
      setInputValue("")
    }

    console.log("messages", messages);

    return (
      <div className="App">
          <h1>Chat</h1>
          <div className='chat-window'>
            {
              messages.map((message, i) => {
                return <div key={i} className={message.className}>{message.body}</div>
              })
            }
          </div>
          <div className='input-wrapper'>
            <input value={inputValue} onChange={e => setInputValue(e.target.value)}/>
            <button onClick={addMessage}>Send</button>
          </div>
      </div>
    );
}

export default App;

Upvotes: 0

Views: 241

Answers (1)

jme11
jme11

Reputation: 17397

You have a closure, so the value of messages is always the initial value of an empty array. Change your code so that you're getting the current value of messages each time:

socket.onmessage = msg => {
  const parsedMessage = JSON.parse(msg.data)
  setMessages((prevState) => [...prevState, {className: "other-message", body: parsedMessage.body}])
};

Upvotes: 1

Related Questions