简体   繁体   English

SocketIO - 后端 emit() 事件未到达 React 客户端

[英]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.很长一段时间以来,我一直在尝试使用 SocketIO 在 React 和 Express 中实现聊天,并且我非常接近完成,但不幸的是,我遇到了死胡同,因为应该工作的 function 不起作用。

I can't seem to be able to link emit() from inside a join() to a socket.on on the client (React).我似乎无法将emit()join()内部链接到客户端(React)上的socket.on

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() ?emit()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';我正在使用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.我试过到处移动socket.on('getUserRooms') ,但它并没有被启动。

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.您可以通过创建一个单独的文件并使用 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然后,您可以通过导入 WebSocketContext 在任何组件中使用套接字

 import {WebSocketContext} from "path/to/websocket.js" import {useContext} from "react" function AnyOtherComponents(){ let {socket} = useContext(WebSocketContext) useEffect(()=>{ socket.on("event", data=>{ //... }) }) return <></> }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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