user9694049
user9694049

Reputation: 211

Can't update state when using `setFoo` as a callback for websocket `onmessage` event

Goal:

I'm trying to get messages from a server through a websocket connection and add them to an array of objects called outgoingMessages.

Problem:

Only the newest message is saved in outgoingMessages when receive() is called.

I suspect this happens because Websocket.onmessage only uses the value of outgoingMessages from the time when I call connect(). I've tried fiddling around with it quite a bit, but I haven't been able to fix this issue.

How can I get receive() to use the current state, not the initial one?

Thanks in advance.

const Dashboard = () => {
  const [incomingMessages, setIncomingMessages] = React.useState([]);
  const ws = React.useRef(null);

  const receive = (message) => {
    setIncomingMessages([...incomingMessages, message]);
  };

  const connect = () => {
    ws.current = new WebSocket(WS_URL + "mqtt/ws/messages");
    ws.current.onopen = () => setWsConnected(true);
    ws.current.onclose = () => setWsConnected(false);
    ws.current.onmessage = (event) => receive(event);
  };

  return (<>Stuff</>)
}

Upvotes: 1

Views: 1539

Answers (1)

gdh
gdh

Reputation: 13692

Looks like the connect function is called once in the initial render and in the receive function, the incomingMessages value is always obtained from the closure and hence you only see the latest message.

To set state, use functional set state pattern i.e provide a callback to setIncomingMessages.

Like this

const Dashboard = () => {
  const [outgoingMessages, setOutgoingMessages] = React.useState([]);
  const ws = React.useRef(null);

  const receive = (message) => {
    setIncomingMessages(prev => [...prev, message]);
  };

  const connect = () => {
    ws.current = new WebSocket(WS_URL + "mqtt/ws/messages");
    ws.current.onopen = () => setWsConnected(true);
    ws.current.onclose = () => setWsConnected(false);
    ws.current.onmessage = (event) => receive(event);
  };

  return (<>Stuff</>)
}

p.s. -- just a quick note - your state deals with outgoingMessages but you do set state for incomingMessagesinreceive` fun. So have a check on that...

Upvotes: 5

Related Questions