UltraDonut
UltraDonut

Reputation: 61

UseState in useEffect hook with empty array (for socket.io.on)

Currently I have the following code:

export default function Chat() {
    const [chat, setChat] = useState({ ID: 0, messages: [] })
    const {socket} = useContext(SocketContext)

    useEffect(() => {
        socket.on('/chat', (message) => {
            setChat({...chat, messages: [...chat.messages, message] }
            //Some other interaction with variables inside this Chat function (eg: document.getElementById)
        }
    }, [])
 
    return <div>{chat.toDivs()}</div>
}

But this doesn't work as expected, because the useEffect always uses the same value of chat. So the result of receiving a message on /chat will always be [message] instead of [...prevMessages, message].

I have searched about this problem and people suggested to use componentDidMount(), but since I'm not using React.Component extendable class, I can't use that method.

Is there any good, simple solution for this?

Upvotes: 2

Views: 510

Answers (1)

Drew Reese
Drew Reese

Reputation: 202667

Use a functional state update so you are updating from the previous state and not the state value closed over in callback scope.

Don't forget to also unsubscribe when unmounting to clean up resources.

const [chat, setChat] = useState({ ID: 0, messages: [] });

useEffect(() => {
  const onChat = (message) => {
    setChat(chat => ({
      ...chat,
      messages: [...chat.messages, message],
    }));
  };

  socket.on('/chat', onChat);

  return () => socket.off('/chat', onChat);
}, []);

Upvotes: 3

Related Questions