As per the documentation one to one chat happens when we emit the message to the specific socket.id. for example
io.to(socket.id).emit("event","message")
This will only work if two users are connected at the same time. If we are to add chat feature in our application where registered users can chat with each other (1-1).What is the optimal solution for that.I have implemented the chat using io.to(socket.id),but when one of the user is offline socket.id is not available.
-----------------------------------Client-------------------------------
function Chat() {
const [conversations, setConversations] = useState([]);
const [currentChat, setCurrentChat] = useState(null);
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState("");
const [arrivalMessage, setArrivalMessage] = useState(null);
const scrollRef = useRef();
const socket = useRef();
const client = isAuthenticated()
const clientId = isAuthenticated().client._id;
const token = isAuthenticated().client.token
console.log(clientId)
useEffect(() => {
socket.current = io("ws://localhost:8900");
socket.current.on("getMessage", (data) => {
setArrivalMessage({
sender: data.senderId,
text: data.text,
createdAt: Date.now(),
});
});
}, []);
useEffect(() => {
arrivalMessage &&
currentChat?.members.includes(arrivalMessage.sender) &&
setMessages((prev) => [...prev, arrivalMessage]);
}, [arrivalMessage, currentChat]);
useEffect(() => {
socket.current.emit("addUser",clientId );
}, [client]);
useEffect(() => {
getconversation(clientId,token).then((data)=>{
if(data.error){
console.log(data.error)
}else{
console.log(data)
setConversations(data)
}
})
}, [])
useEffect(() => {
const getMessages = async () => {
try {
const res = await axios.get("http://localhost:8080/api/messages/" + currentChat?._id);
setMessages(res.data);
} catch (err) {
console.log(err);
}
};
getMessages();
}, [currentChat]);
useEffect(() => {
scrollRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const handleSubmit = async (e) => {
e.preventDefault();
const message = {
sender:clientId,
text: newMessage,
conversationId: currentChat._id,
};
const receiverId = currentChat.members.find(
(member) => member !== clientId
);
socket.current.emit("sendMessage", {
senderId: clientId,
receiverId,
text: newMessage,
});
try {
const res = await axios.post("http://localhost:8080/api/messages", message);
setMessages([...messages, res.data]);
setNewMessage("");
} catch (err) {
console.log(err);
}
};
-------------------------Server-----------------------------------------
const io = require("socket.io")(8900, {
cors: {
origin: "http://localhost:3000",
},
});
let users = [];
const addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) &&
users.push({ userId, socketId });
};
const removeUser = (socketId) => {
users = users.filter((user) => user.socketId !== socketId);
};
const getUser = (userId) => {
return users.find((user) => user.userId === userId);
};
io.on("connection", (socket) => {
//when connect
console.log("a user connected."+socket.id);
//take userId and socketId from user
socket.on("addUser", (userId) => {
addUser(userId, socket.id);
io.emit("getUsers", users);
});
//send and get message
socket.on("sendMessage", ({ senderId, receiverId, text }) => {
console.log("receiverid=====>>>",receiverId)
console.log("users ",users)
const user = getUser(receiverId);
console.log("userSocket Id",user)
io.to(user.socketId).emit("getMessage", {
senderId,
text,
});
});
//when disconnect
socket.on("disconnect", () => {
console.log("a user disconnected!");
removeUser(socket.id);
io.emit("getUsers", users);
});
});
First it is needed to store the messages within your app, along with the details of the sender, receiver and timestamp. Then use the web sockets / socket.io to complement the functionality and inform each user real time, if they are online. This approach is what other chat apps follow eg Slack.
So the main properties of your app should be,
About the emitting part, use unique identifiers eg a unique identifier (uuid) of the session generated between one or more users, to target a room (the users should listen to this identifier) or the user ids and emit to each user id (the user can listen to their own user identifier once they are online). The details of the message should be enough to help the app render it on the right screen.