Search code examples
socket.ioreact-hooksspread-syntaxuse-effect

How can I append an item to an array in a React component using hooks?


I'm trying to build a chat room using Socket.io and React (hooks). I have events being emitted to and from the server, but when I try to update my messages array on the client side, the array is constantly replaced, rather than updated.

Client (React)

const socket = io(process.env.REACT_APP_API_URL);
const [currentMessage, setCurrentMessage] = useState(null);
const [messages, setMessages] = useState([]);

function sendMessage(e) {
  e.preventDefault();

  // emit message to server
  socket.emit('add', currentMessage);
}

useEffect(() => {

  // listen for add message event from server
  socket.on('add', function(msg) {
    // add new message to messages array
    setMessages([...messages, msg]);
  });

}, []);

// loop through messages
const items = messages.map((msg, key) =>
  <li key={key}>{msg}</li>
);

return (
  <ul className="chat">
    {items}
  </ul>
  <input type="text" onChange={(e) => setCurrentMessage(e.target.value)}>
  <button type="submit" onClick={(e) => sendMessage(e)} />
);

Again, the message is received from the server successfully, but my messages array only ever has one item in it - the latest message. I thought using the spread operator (i.e. setMessages([...messages, msg])) would keep the other array items. What am I not seeing?


Solution

  • Your code is equivalent to setMessages([...[], msg]) because messages is a constant (see the const keyword) within 1 render, and you only run the function inside useEffect in the first render when it was [].

    Use functional updates:

    setMessages((currentMessages) => [...currentMessages, msg])