I have been trying for a long time to implement a chat in React and Express using SocketIO and I am very close to finish, but unfortunately I've hit a dead end because a function that should work doesn't.
I can't seem to be able to link emit()
from inside a join()
to a socket.on
on the client (React).
I have been following some guides, but their code just works when mine does not.
Why can't I get anything in the client when emit()
is inside a join()
?
'use strict';
import express from 'express';
import { Server } from 'socket.io';
import { createServer } from 'http';
import cors from 'cors';
import bodyParser from 'body-parser';
import config from './config.js';
import authRoutes from './routes/authRoutes.js';
import userRoutes from './routes/userRoutes.js';
import roomRoutes from './routes/roomRoutes.js';
import chatRoutes from './routes/chatRoutes.js';
const app = express();
const server = createServer(app);
const io = new Server(server, {
cors: {
origin: ['http://localhost:3000', 'http://localhost:3001'],
},
});
app.use(express.json());
app.use(cors());
app.use(bodyParser.json());
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.use('/api/rooms', roomRoutes);
app.use('/api/chat', chatRoutes);
const users = [];
const userJoin = (id, room, socketId, name) => {
const user = { id, room, socketId, name };
!users.some(
(user) => user.id === id && user.room === room && user.name === name
) && users.push(user);
return user;
};
const getCurrentUser = (socketId) => {
return users.find((user) => user.socketId === socketId);
};
const userLeave = (socketId) => {
const index = users.findIndex((user) => user.socketId === socketId);
if (index !== -1) {
return users.splice(index, 1)[0];
}
};
const getRoomUsers = (room) => {
return users.filter((user) => user.room === room);
};
const messageFormat = (messageText, sentBy) => {
const message = { messageText, sentBy, timestamp: new Date().toUTCString() };
return message;
};
const botName = 'BOT';
io.on('connection', (socket) => {
// Connecting
console.log('A user has connected.');
// Join room
socket.on('joinRoom', ({ id, chatID, name }) => {
const user = userJoin(id, chatID, socket.id, name);
socket.join(user.room, () => {
// socket.emit('getMessage', messageFormat('Welcome to Landmarks', botName));
// socket.broadcast
// .to(user.room)
// .emit('sendMessage', messageFormat(`${user.name} has joined the chat.`, botName));
// Send user and room info
io.to(user.room).emit('getUserRooms', {
room: user.room,
users: getRoomUsers(user.room),
});
// socket.on('sendUserRooms', () => {
// const user = getCurrentUser(socket.id);
// console.log(user);
// });
});
});
// Send message
socket.on('sendMessage', ({ sentBy, messageText }) => {
const user = getCurrentUser(socket.id);
io.to(user.room).emit('getMessage', messageFormat(messageText, user.id));
});
// Disconnecting
socket.on('disconnect', () => {
const user = userLeave(socket.id);
if (user) {
io.to(user.room).emit(
'sendMessage',
messageFormat(`${user.name} left the chat.`, user.id)
);
io.to(user.room).emit('getUserRooms', {
room: user.room,
users: getRoomUsers(user.room),
});
}
console.log('User has disconnected');
});
});
server.listen(config.port, () => {
console.log(`App is listening on url http://localhost:${config.port}`);
});
This is how my app is structured:
function App() {
return (
<BrowserRouter>
<Routes>
<Route
path='/'
element={
<PrivateRoute>
<Home />
</PrivateRoute>
}
>
<Route index element={<LandingPage />} />
<Route path='rooms' element={<LandingPage />}>
<Route path='new' element={<CreateRoom />} />
<Route path=':roomID' element={<RoomItem />} />
<Route path=':roomID/edit' element={<EditRoom />} />
<Route path='join/:inviteToken/' element={<JoinRoom />} />
</Route>
<Route path='profile/:id' element={<Profile />} />
</Route>
<Route path='login' element={<Login />} />
<Route path='register' element={<Register />} />
<Route path='set-avatar' element={<SetAvatar />} />
<Route path='*' element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
Inside Room Item is Drawer and inside Drawer are both Chat and Members.
// inside Room Item this is the only socketIO call
useEffect(() => {
if (currentRoom) {
dispatch(getMessages(currentRoom.chatID));
dispatch(roomActions.setCurrentChat(currentRoom.chatID));
dispatch(chatActions.reset());
}
}, [currentRoom, dispatch, currentUserID]);
useEffect(() => {
if (currentChat) {
socket.emit('joinRoom', {
id: currentUserID,
chatID: currentChat,
name: name,
});
}
console.log('do i get here');
}, [currentChat, currentUserID, name]);
I am importing socket using import socket from '../SocketIO/socket';
with
import io from 'socket.io-client';
const socket = io('ws://localhost:8080');
export default socket;
And here is the Members component. Here I am only trying to get which users are in which rooms:
import { List } from 'antd';
import { useEffect } from 'react';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import socket from '../../SocketIO/socket';
import { roomActions } from '../roomSlice';
import classes from './Members.module.css';
export default function Members({ members, ownerID, currentUserID, chatID }) {
const { onlineUsers } = useSelector((state) => state.room);
const [online, setOnline] = useState([]);
const [offline, setOffline] = useState([]);
const dispatch = useDispatch();
socket.on('getUserRooms', ({ room: roomID, users }) => {
dispatch(roomActions.setOnlineUsers({ room: roomID, users }));
console.log('here');
});
useEffect(() => {
console.log(onlineUsers);
setOnline(
members.filter((el) => {
return onlineUsers.some((foo) => foo.id === el.id);
})
);
setOffline(
members.filter((el) => {
return onlineUsers.every((foo) => foo.id !== el.id);
})
);
}, [onlineUsers, members, chatID]);
// console.log('online', online);
// console.log('offline', offline);
return (<></>);
I have tried moving socket.on('getUserRooms')
everywhere, but it just doesn't gets fired up.
You need to make your socket available everywhere I guess. You can achieve this by creating a separate file and defining a react context using createContext.
//websocket.js import React, {createContext } from 'react' import openSocket from "socket.io-client"; const ENDPOINT = "http://localhost:3000/" const WebSocketContext = createContext(null) export { WebSocketContext } export default ({ children }) => { let socket; let ws; if (,socket) { socket = openSocket(ENDPOINT: {transports.["websocket"]}) socket,on("eventname". data=>{ console:log(data) }) ws = { socket, socket. } } return ( <WebSocketContext.Provider value={ws}> {children} </WebSocketContext.Provider> ) }
Then, you wrap the context around your routes so you can use socket inside the components.
import WebSocketProvider from "path/to/websocket.js" function App(){ <WebSocketProvider> <BrowserRouter> //... </BrowserRouter> </WebSocketProvider> }
you can then use the socket inside any of the components by importing the WebSocketContext
import {WebSocketContext} from "path/to/websocket.js" import {useContext} from "react" function AnyOtherComponents(){ let {socket} = useContext(WebSocketContext) useEffect(()=>{ socket.on("event", data=>{ //... }) }) return <></> }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.