Frameworks used: Next.js, Socket.io, React
I am making a simple messaging app. I am basically just emitting a message someone types, sending that message to the server, which is then "broadcasted" back through an event called "receive-chat-message". The issue I'm having is when the response is handled on the front end with "receive-chat-message", the [messages] state is not appending a new message to display, it's just overwriting the state.
My goal is to send a message to the server; the server then sends the message back to the client. I then append the new message on the client, sent from the server, to the [messages] state on the client, and then render the state of [messages]. The problem is it's only overwriting the [messages] state, and not appending new messages to it.
Code that sends message to the server
const [message, setMessage] = useState("");
const handleChange = (e) => {
setMessage(e.target.value);
};
const submitMessage = async () => {
// socket.io
const socket = io();
socket.emit("send-chat-message", message, user);
setMessage("");
};
Front-End Code
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = io();
socket.on("receive-chat-message", handleSetMessage);
return () => socket.disconnect();
}, []);
const handleSetMessage = (data) => {
/* data prop is the new message - it has a {message: "message", user: "name"}
sent from server.
THIS IS THE PROBLEM IN THE CODE WHERE IT DOESN'T APPEND
NEW MESSAGES TO THE [messages] STATE, IT JUST OVERWRITES
THE CURRENT STATE WITH THE NEW MESSAGE. */
setMessages([...messages, data]);
};
Back-End Code
export default async function handler(req, res) {
if (!res.socket.server.io) {
const io = new Server(res.socket.server);
res.socket.server.io = io;
io.on("connection", async (socket) => {
socket.on("send-chat-message", async (message, user) => {
socket.broadcast.emit("recieve-chat-message",
{
message,
name: user && user.name,
});
});
});
}
}
I solved this by setting messages through the functional method like:
setMessages((messages) => [...messages, data]);
Instead of:
setMessages([...messages, data])
I think we need this because I was updating the state from a function called within a socket.io listener and was never actually grabbing the previous state; so I had to direct it to the previous state and then merge the values.