skip to Main Content

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


  1. You are never listening for the "new message recived" event on the client in that first useEffect

    Login or Signup to reply.
  2. I believe in handlesendMessage this is how you emit new message

     soket.emit("new mesaage", data);
    

    you are sending this data to the socket server. On the server, you should have

    socket.on("new mesaage", (data) => {
          // you need write a handler
          newMessageHandler(socket, data);
        });
    

    this is the part that you have to implement. A pseudo code will be

    const newMessageHandler = async (socket, data) => {
      try {
        const { userId } = socket.user;
        const { receiverUserId, content } = data;
        // save the message to the db
        const message = await Message.create({
          content,
          author: userId,
          // ...
        });
        // FIND IF CONVERSATION EXISTS BETWEEN TWO USERS
        
        // If CONVERSATION exists perform an update to sender and receiver if it is online
        
        // If Conversation does not exist create newconverstation 
        
        // Update the chat history
        }
      } catch (error) {
        console.error(error);
      }
    };
    

    you update the database and other user will always make a request to an endpoint to get the latest data.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search