Socket.io server emitting an empty array to the client

Socket.io server emitting an empty array to the client

  

In the input box of my app, I type a message and expect the message to be transmitted to the server and then transmitted back to the client so as to map the information to my UI.

While transmitting to the server seems to be working correctly, getting back the information from the socket.io server does not seem to work and returns an empty array.

Now the thing is when I input a message the first time, the server returns an empty array but when I input another message, the server now returns an array with one object instead of two. Also, the object it returns is the first message I typed initially and not the new one.

To sum everything up, server returns one object lesser than what it was given and what it returns is what it was given initially and not the new information.

here

This is the server console at the first instant which appears to work correctly

Here is the client console at the second instant which returns what was passed in the first instance

This is the server console at the second instant which also appear to work correctly

Here is the client code:

'use client'

import { io } from 'socket.io-client'
import { useEffect, useState } from 'react'
import { ChatSenderBox } from './ChatBox'
import { ChatRecieverBox } from './ChatBox'
import MessageInput from './MessageInput'


const ChatContainer = ({username, userImage}) => {

  const [socket, setSocket] = useState(undefined)
  const [allInfo, setAllInfo] = useState([])

  // sending all info to the socket
  const sendChatToServer = (chat) => {
    socket.emit('chat', chat)
  }

  //getting back the emitted message
  useEffect(()=>{
    const socket = io('http://localhost:3001')

    socket.on('chat', (chatRecieved) => {
      setAllInfo(chatRecieved)
    })

    setSocket(socket)
  }, [])

  // This is a function passed as prop to the messageInput component
  const addMessage = (chat) => {
    const newChat = {...chat, username, userImage}

    if(newChat.message !== ''){

      setAllInfo([...allInfo, newChat])
  
      sendChatToServer([...allInfo, newChat])

      console.log(allInfo)
    } 
  }

  const ChatsArrangment = () => {
    return allInfo.map((info, index) => {
      if(info.username === username) return 
        <ChatSenderBox
          key={index}
          username={info.username}
          profilePic={info.userImage}
          message={info.message}
        />

        return
          <ChatRecieverBox
            key={index}
            username={info.username}
            profilePic={info.userImage}
            message={info.message}
          />  
    })
  }

  return (
    <div className=' w-full h-full p-4 chatPage'>

      <ChatsArrangment />

      <MessageInput addMessage={addMessage}/>
    </div>
  )
}

export default ChatContainer

And this is the server code:

const express = require("express")
const http = require("http")
const { Server } = require("socket.io");
const app = express()

const server = http.createServer(app)
const io = new Server(server, {
    cors:{
        origin: 'http://localhost:3000',
        methods: ["GET", "POST"]
    }
})

io.on("connection", (socket) => {
    console.log("A user is connected")

    // messaged recieved and sent back to the client.
    socket.on("chat", (chat)=>{
        if(chat.lenght){
            io.emit("chat", chat)
        }
        console.log(chat)
    })

    socket.on("disconnect", ()=>{
        console.log("A user disconnected")
    })
})

server.listen(3001, () => console.log('listening to port 3001'))

Answer

Actually your code works fine, the problem is in the placement of console

In react setState here (setAllInfo) function is asynchronous, so the state changes won't be applied immediately. That's why the console after setState still in old value.

Solution

Simply try to console outside of setState or use useEffect and add the value as dependency

useEffect(() => {
  console.log(allInfo); // Updated value here
}, [allInfo]);

Also there is an issue in your react component, you have to use either inline js or pass allinfo as a prop like below.

  1. Inline condition

     return (
         <div className=" w-full h-full p-4 chatPage">
             {allInfo.length &&
                 allInfo.map((info, index) => (
                     <ChatSenderBox
                         key={index}
                         username={info.username}
                         profilePic={info.userImage}
                         message={info.message}
                     />
                 ))}
             <MessageInput addMessage={addMessage} />
         </div>
     );
    
  2. Or pass allinfo as prop the component ChatsArrangment so it can render whenever the allinfo changes.

     const ChatsArrangment = (data) => {
         return data.map((info, index) => (
             <ChatSenderBox key={index} username={info.username} profilePic={info.userImage} message={info.message} />
         ));
     };
    
     return (
         <div className=" w-full h-full p-4 chatPage">
             {allinfo.length && <ChatsArrangment data={allinfo} />}
             <MessageInput addMessage={addMessage} />
         </div>
     );
    
© 2024 Dagalaxy. All rights reserved.