In this episode of building a Chat, we are going to go over how to implement Web sockets in a group chat. Before I did this, I had this unnecessary phobia about how I was going to go about it. I mean, for 1-1 chat we could just emit the socket to the other party. But we have a group, full of people, do we emit the socket to an array of members individually? That would be expensive to do.
The optimal solution involves the use of socket.join method. When a user opens a Chat, he essentially joins the chat id - if any. Or you can identify it with a socket id.
DEMO:
My group chat has an ID so I just had to create a useEffect on the group chat page that emits an event to the backend, my server runs on port 5000.
socket.current = io("http://localhost:5000")
socket.current?.emit("join-chat", selectedGroup._id)
This is wrapped in a useEffect function. Selected Group Id is the ID of the group, could as well be the default generated Socket ID.
On the server is where we use the socket join function, this is the most important part. The user joins the chat
socket.on("join-chat", (id)=> {
socket.join(id)
console.log(`A user joined ${id}`)
})
The id is the selected Group Id coming from our frontend.
When a new message is sent, We update the chat state so the screen can be updated with the sent message. All these is writing in the sendChat arrow function
dispatch(sendMsg({from:user._id, groupId: selectedGroup._id, message: message, userId: user._id}))
groupChats = [...groupChats];
groupChats.push({fromSelf:true, message: message})
dispatch(addGroupMessage(groupChats))
setMessage("");
We define a payload
let payload = {id:selectedGroup._id, data: {
message:message, avatarImage: user.avatarImage,
nickname: user.name, id: user._id
}
}
Then we emit the event to the backend. The groupId is contained in the payload.
socket.current.emit("send-groupMsg", payload)
I know right, I'm bad at naming.
On the server
socket.on("send-groupMsg", (data)=> {
socket.broadcast.to(data.id).emit("message", data.data)
})
We emit the message event to the groupId, the socket.broadcast.to emits the event to all users but the sender. This is a very useful feature.
In the same group chat component, in another useEffect function we want to listen to the socket
useEffect(() =>{
socket.current.on("message", (data)=> {
groupChats = [...groupChats];
groupChats.push({fromSelf:false, message: data.message, details: {
avatarImage: data.avatarImage,
nickname: data.nickname,
_id: data.id
}})
dispatch(addGroupMessage(groupChats))
}
}, [groupChats] )
The groupChats in the useEffect dependency array is vital. I encountered this bug when I failed to put it
The current chat list won't be gotten when there's a new socket if we don't put it
And that's about it, we update the state with the incoming message so that our chat screen can get updated in real time.
Follow for more❤️