I am developing a realtime chat in nodejs and react and socket io. In theory the chat is working, but the problem is that the sender’s message only appears on the screen when the recipient sends a message too.
ex : I have browser 1 and browser 2. Every time browser 1 sends messages, these messages appear on its screen, but these messages only appear for browser 2 when browser 2 sends a message.
const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); const dotenv = require('dotenv') const app = express(); const mongoose = require('mongoose'); const http = require('http'); const server = http.createServer(app); const { Server } = require('socket.io'); const io = new Server(server, { cors: { origin: ['http://localhost:3000'], } }); dotenv.config(); const nameRoutes = require('./routes/nameRoutes'); app.use(express.json()); app.use(cors()); app.use(bodyParser()); app.use('/name', nameRoutes); app.use('/', (req, res) => { res.status(200).json({ msg: 'API IS ALIVE!' }); }) let messages = [] io.on('connection', (socket) => { socket.on('send-message', (data) => { messages.push(data); socket.emit('message-from-server', messages); console.log(messages); }) }); async function connection () { const uri = process.env.MONGO_URI; const port = process.env.PORT; try { await mongoose.connect(uri); console.log('Connected to database'); server.listen(port, () => { console.log(`Listening on port ${port}`) }); } catch (err) { console.log(err); } } connection(); module.exports = app;
react/socketio
import * as C from './styles'; import { io } from 'socket.io-client'; import { useNavigate } from 'react-router-dom'; import { useEffect, useState } from 'react'; const Chat = () => { const navigate = useNavigate(); const [message, setMessage] = useState(''); const [chatMessages, setChatMessages] = useState([]); const [socket, setSocket] = useState(null); useEffect(() => { setSocket(io("http://localhost:3010")); }, []); useEffect(() => { if (socket) { socket.on('message-from-server', (data) => { setChatMessages(data); }) } }, [socket]) const handleSendMessage = () => { if (message) { socket.emit('send-message', { message }); setMessage(''); } } const handleExitChat = () => { navigate('/'); } return ( <C.Container> <C.MessagesChat> <h2>Messages</h2> </C.MessagesChat> {chatMessages.map(text => { return ( <C.Messages> <p>{text.message}</p> </C.Messages> ) })} <C.MessageText> <textarea name="" id="" cols="30" rows="10" placeholder='Digite sua mensagem...' value={message} onChange={(e) => setMessage(e.target.value)} ></textarea> <button onClick={handleSendMessage}>Enviar</button> </C.MessageText> </C.Container> ) } export default Chat;
Advertisement
Answer
You’re using
socket.emit('message-from-server', messages);
Try using io.emit
instead, which broadcasts to all attached sockets. Socket.emit is used to send a message only to the called socket. In this case, that means the socket that first sent the message. This can be helpful for if you just want to notify that individual socket that something processed correctly, etc., but it isn’t useful for when you want to notify other users of a socket’s actitivies.
EDIT: Here’s a link to the docs that describe your various emit options: https://socket.io/docs/v4/broadcasting-events/
Here’s what your server-side code should look like:
io.on('connection', (socket) => { // Define our routes first, but note the key new emit below this listener socket.on('send-message', (data) => { messages.push(data); // Notify all connected users each time a message is posted io.emit('message-from-server', messages); console.log(messages); } // THIS IS NEW // This is your initial load, to get the messages when you open the page. socket.emit('message-from-server', messages); }
Note the difference between the io.emit
that I use in the listener and the socket.emit
that I use when first connecting.