Fiddle Freak
Fiddle Freak

Reputation: 2041

message sent but not received socket.on(...) for client

(Working) I can see the message was sent to the server just fine:

enter image description here

// function used in reactjs(client)
const sendMessage = () => {
  const data = {
    room: room,
    content: {
      author: userName,
      message: message
    }
  }
  Promise.resolve(socket.emit('send_message', data))
  .then(() => setMessage(''));
}

useEffect(() => {
  socket.on('receive_message', data => {
    console.log(data);
  });
}, []);
// function used in index.js(server)
io.on('connection', (socket) => {
  console.log(socket.id);

  socket.on('join_room', (roomName) => {
    socket.join(roomName); // creates room with the name
    console.log('User Joined Room:', roomName);
  });

  socket.on('send_message', (data) => {
    console.log("data =", data);
    socket.to(data.room).emit('receive_message', data.content);
  });

  socket.on('disconnect', () => {
    console.log('USER DISCONNECTED')
  });
});

(Problem) Can anyone tell me why receive_message is not showing up in the console.log?

The Full Code of both files

index.js(server)

const express = require('express');
const morgan = require('morgan');
const { createServer } = require('http');
const { Server } = require('socket.io');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());
app.use(morgan('dev'));
const httpServer = createServer(app);

const io = new Server(httpServer, {
  cors: true,
  origins: ['http://localhost:3000']
});

io.on('connection', (socket) => {
  console.log(socket.id);

  socket.on('join_room', (roomName) => {
    socket.join(roomName); // creates room with the name
    console.log('User Joined Room:', roomName);
  });

  socket.on('send_message', (data) => {
    console.log("data =", data);
    socket.to(data.room).emit('receive_message', data.content);
  });

  socket.on('disconnect', () => {
    console.log('USER DISCONNECTED')
  });
});

httpServer.listen(3002, () => console.log(`Socket.io test running on port 3002`))

app.js(client)

import React, { useEffect, useState } from 'react';
import io from 'socket.io-client';
import './App.css';

let socket = io('localhost:3002/');

function App() {
  // variables
  const [loggedIn, setLoggedIn] = useState(false);
  const [room, setRoom] = useState('');
  const [userName, setUserName] = useState('');
  const [message, setMessage] = useState('');
  const [messageList, setMessageList] = useState([]);

  // functions
  const connectToRoom = () => {
    console.log('button clicked');
    setLoggedIn(true);
    socket.emit('join_room', room); // join_room copied from backend
  }

  const sendMessage = () => {
    const data = {
      room: room,
      content: {
        author: userName,
        message: message
      }
    }
    Promise.resolve(socket.emit('send_message', data))
    .then(() => setMessage(''));
  }

  // setup
  useEffect(() => {
    socket.on('receive_message', data => {
      console.log(data);
    });
  }, []);

  return (
    <div className="App">
      {!loggedIn ?
        <div className="logIn">
          <div className="inputs">
            <input type="text" placeholder="Name..." value={userName} onChange={ev => setUserName(ev.target.value)} />
            <input type="text" placeholder="Room..." value={room} onChange={ev => setRoom(ev.target.value)} />
          </div>
          <button onClick={connectToRoom}>Enter Chat</button>
        </div> :
        <div className="chatContainer">
          <div className="messages">
            <h1>{room}</h1>
            {messageList.map((messageInfo, index) => 
              <div key={index} className="messageBox">
                <p>{messageInfo.author}:&nbsp;{messageInfo.message}</p>
              </div>
            )}
          </div>
          <div className="messageInputs">
            <input type="text" placeholder="Message..." value={message} onChange={ev => setMessage(ev.target.value)} />
            <button onClick={sendMessage}>Send</button>
          </div>
        </div>
      }
    </div>
  );
}

export default App;

Upvotes: 0

Views: 904

Answers (1)

Fiddle Freak
Fiddle Freak

Reputation: 2041

I found the issue. The state is managed in two different places on the client side.

For the socket.on('receive_message', ...) in useEffect, this will show up on every single other browser console logged into the current room except the current browser's console(the browser sending the message).

To also get it to show in current console browser window, I also needed to add the state in where I am submitting it.

const sendMessage = () => {
  const data = {
    room: room,
    content: {
      author: userName,
      message: message
    }
  }
  Promise.resolve(socket.emit('send_message', data))
  .then(() => console.log(data)) // <------- add this line here
  .then(() => setMessage(''));
}

This of course means setMessageList will need to be called both inside the sendMessage() and the socket.on('receive_message',...)(within the useEffect())

However, be careful when calling setMessageList inside socket.on('receive_message',...). The reasons for this is because it is called within a single useEffect one time, it captures the state from when that function was called and will not update. In other words, you cannot use setMessageList([...messageList, data]) because this function will only read messageList from when this function was first called initially.

To get around this, I see two options...

  1. You can remove the [] inside the useEffect((...), []) forcing it to run in an infinite loop (this seems expensive).
  2. The other option is to have the data on the backend update, save, then send back to which the setMessageList(data)(inside socket.on('receive_message',...)) will have to reload all the messages and re-render them all. (still seems expensive, but not as much as the first option)

Upvotes: 1

Related Questions