I am trying to create a real-time chat app. I set up my soket.io for the server as well as for client-side code, I am using react. I have several consoles and all of them logging the correct data. However if I send the message, new message gets displayed on my chat automatically, but the user to whom I am sending it to needs to either refresh the page or reselect the chat, which is, of course, something I am not looking for. In summary my main problem is that new message is not displayed on addressee chat without refreshing or reselecting the chat. here is both my server-side and client(react)-side code, and if someone will be kind enough to help me detect the issue I will be more than happy.
I tried so many ways but none of them worked …
my client-side code:
import React,{useState,useEffect} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getAllUserChats, selectSelectedChat, SET_SELCTED_CHAT_TO_NULL } from '../../redux/chat/chatSlice';
import { selectUser } from '../../redux/auth/authSlice';
import { Box, Wrap, WrapItem } from '@chakra-ui/layout';
import { Text, IconButton, Spinner, FormControl, Input } from '@chakra-ui/react';
import { BiLeftArrow } from "react-icons/bi";
import ProfileModal from '../modal/ProfileModal';
import { Avatar} from '@chakra-ui/react'
import UpdateGroupModal from '../modal/UpdateGroupModal';
import { sendMessage } from '../../redux/messages/MessagesService';
import { fertchAllMessages, selectAllMessages, selectMessageREcive, SET_ALL_MESSAGES, SET_MESSAGERECIVE } from '../../redux/messages/MessagesSlice';
import ScrolableChat from '../scrollableChat/ScrolableChat';
import io from "socket.io-client";
const ENDPOINT = `http://localhost:5000`;
var soket;
var selectedChatCompare;
let newMsg;
const SingleChat = () => {
const [load, setLoad] = useState(false);
const [messages, setmesaages] = useState([]);
const [message,setMessage] = useState()
const selectedChat = useSelector(selectSelectedChat);
const [soketConnected,setSoketConnected] = useState(false)
const user = useSelector(selectUser);
const everyMessage = useSelector(selectAllMessages);
const dispatch = useDispatch();
const messageRec = useSelector(selectMessageREcive);
useEffect(() => {
soket = io(ENDPOINT);
soket.on("connect", () => {
console.log("Socket connected:", soket.id);
soket.emit("setup", user);
});
soket.on("disconnect", () => {
console.log("Socket disconnected");
});
soket.on("connection", () => {
setSoketConnected(true);
console.log("Connection established");
});
soket.on("message", (message) => {
console.log("Received message:", message);
// handle the received message
});
return () => {
soket.disconnect();
};
}, []);
useEffect(() => {
async function gettingAllMessages() {
setLoad(true);
const data = await dispatch(fertchAllMessages(selectedChat !== null && selectedChat?._id));
dispatch(SET_ALL_MESSAGES(data?.payload));
setmesaages(data?.payload);
setLoad(false);
soket.emit("Join chat", selectedChat?._id);
}
gettingAllMessages();
selectedChatCompare = selectedChat;
}, [dispatch, selectedChat]);
const handlesendMessage = async (event) => {
if (event.key === "Enter" && message !== "") {
const formdata = {
chatId: selectedChat?._id,
content: message,
};
setMessage("");
try {
const data = await dispatch(sendMessage(formdata));
soket.emit("new mesaage", data);
newMsg = data
await dispatch(SET_ALL_MESSAGES([data]));
setmesaages((prevMessages) => [...prevMessages, data]);
} catch (error) {
console.log(error.message);
}
}
};
useEffect(() => {
if (!soket) return;
soket.on("new message received", (newMessageReceived) => {
dispatch(SET_ALL_MESSAGES([newMessageReceived]));
setmesaages((prevMessages) => [...prevMessages, newMessageReceived]);
});
return () => {
soket.off("new message received");
};
},);
// rest of the code remains the same
const handleTyping = (e) => {
setMessage(e.target.value)
};
function getUser() {
return selectedChat !== null && user?._id === selectedChat?.users[0]?._id ? selectedChat?.users[1] : selectedChat?.users[0];
}
function getUserImage() {
return selectedChat !== null && user?._id === selectedChat?.users[0]?._id ? selectedChat?.users[1]?.picture?.filePath && selectedChat?.users[1]?.picture?.filePath : selectedChat?.users[0]?.picture?.filePath && selectedChat?.users[0]?.picture?.filePath ;
}
const handleSelection = async () => {
await dispatch(SET_SELCTED_CHAT_TO_NULL())
}
return <>
{
selectedChat !== null ? (
<>
<Text
fontSize={{ base: "28px", md: "30px" }}
pb={3}
px={2}
width="100%"
fontFamily="Work sans"
display={"flex"}
justifyContent={{ base: "space-between" }}
alignItems="center">
<IconButton display={{ base: "flex", md: "none" }}
icon={<BiLeftArrow />} onClick={() => handleSelection()} />
{!selectedChat?.isGroupChat ?
(<>
{user?._id === selectedChat?.users[0]?._id ?selectedChat?.users[1]?.name : selectedChat?.users[0]?.name }
<ProfileModal user={getUser()} >
<Wrap>
<WrapItem>
<Avatar cursor={"pointer"} border={"2px"} size={"md"} src={getUserImage()} />
</WrapItem>
</Wrap>
</ProfileModal>
</>) :
(<>
{selectedChat?.chatName.toUpperCase()}
<UpdateGroupModal/>
</>)}
</Text>
<Box
display="flex"
flexDir="column"
justifyContent="flex-end"
p={3}
bg="linear-gradient(to right top, #3a5a89, #005872, #0e5152, #2a4638, #353a2b)"
w="100%"
h="100%"
borderRadius="lg"
overflowY="hidden"
>
{load ? (<Spinner size={"xl"} width={60} height={60} alignSelf={"center"} margin={"auto"} color="white" borderWidth={"8px"} />) :
(<Box display={"flex"} flexDir={"column"} overflowY={"scroll"} overflow={"hidden"} width={"100%"} height={"100%"} >
<ScrolableChat messages={messages} />
</Box>)
}
<FormControl onKeyDown={(event)=> handlesendMessage(event)} mt={3} >
<Input variant={"filled"} background="E0E0E0" placeholder='send message' _focus={{ bg: "transparent", borderBlockColor: "white", outline:"none" }} color="white" value={message} onChange={(e) => handleTyping(e)} />
</FormControl>
</Box>
</>
) : (
<>
<Box display={"flex"} alignItems={"center"} justifyContent={"center"} height={"100%"} >
<Text fontSize={"3xl"} pb={3} fontFamily={"work sans"} >
Click on the wanted chat and start conversation
</Text>
</Box>
</>
)
}
</>
}
export default SingleChat
my server side code:
const dotenv = require('dotenv').config();
const express = require('express');
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const path = require("path");
const http = require('http');
const socketio = require('socket.io');
const userRoutes = require("./routes/userRoutes");
const contactUsRoutes = require("./routes/contactUsRoute");
const chatRoutes = require("./routes/chatRoutes");
const messageRoutes = require("./routes/messageRoutes");
const errorHandler = require("./middleWare/errorHandler");
const app = express();
const server = http.createServer(app);
const io = socketio(server, {
pingTimeout: 60000,
cors: {
origin: ["http://localhost:3000", "http://shopper-tools.vercel.app"],
credentials: true,
}
});
const PORT = process.env.PORT || 5000;
const MONGO_URL = process.env.MONGO_URI;
// connect to mongo
mongoose.set('strictQuery',false);
// middlwares
app.use(express.json());
app.use(cookieParser());
app.use(express.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(cors({
origin: ["http://localhost:3000", "http://shopper-tools.vercel.app"],
credentials: true
}));
app.use((req, res, next) => {
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
res.header("Access-Control-Allow-Credentials", "true");
next();
});
// upload folder directory
app.use("/uploads",express.static(path.join(__dirname,"uploads")));
// Routes middleware
app.use("/api/users",userRoutes);
app.use("/api/contactus", contactUsRoutes);
app.use("/api/chats", chatRoutes);
app.use("/api/messages",messageRoutes)
app.get("/",(req,res)=>{
res.send("Home Page")
});
// Error middleware
app.use(errorHandler);
// Socket.io setup
io.on('connection', (socket) => {
console.log(`User ${socket.id} connected`);
socket.on("setup", (userData) => {
socket.join(userData?._id);
console.log(userData?._id)
socket.emit("connected")
});
socket.on("Join chat", (room) => {
socket.join(room);
console.log("user just joined", room)
});
socket.on("new mesaage", (newMEssageRecived) => {
var chat = newMEssageRecived?.chat;
if(!chat?.users){
return console.log("no cat.users")
};
chat?.users.forEach(user => {
if (user?._id === newMEssageRecived?.sender?._id) {
return;
};
io.to(user?._id).emit("new message recived", newMEssageRecived);
console.log(user?._id, "user id", newMEssageRecived)
});
})
});
mongoose
.connect(MONGO_URL)
.then(()=>{
server.listen(PORT,()=>{
console.log(`server is connected to port ${PORT}`)
})
})
.catch(err=>{
console.log(err)
});
2
Answers
You are never listening for the "new message recived" event on the client in that first useEffect
I believe in
handlesendMessage
this is how you emit new messageyou are sending this data to the socket server. On the server, you should have
this is the part that you have to implement. A pseudo code will be
you update the database and other user will always make a request to an endpoint to get the latest data.