简体   繁体   中英

SocketIO - Backend emit() event doesn't reach React client

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM