使用 mern 堆栈的聊天应用程序和 socket.io 在发送超过 20 条消息后变慢

chat app using mern stack and socket.io getting slow after sending more than 20 messages

我正在使用 MERN 堆栈和 socket.io 构建实时聊天应用程序。发送大约 20 条消息后,应用程序变慢,当其他用户仅在收到已发送的消息后发送另一条消息时,用户无法编写和发送消息。 这是我的聊天组件

import React, {useEffect, useRef, useState} from 'react';
import Messages from "../Messages/Messages";
import axios from 'axios';
import io from "socket.io-client";

const socket = io('http://localhost:5000/')
function Chat(props) {
    const [messages, setMessages] = useState([])
    const [input, setInput] = useState("")
    useEffect(() => {
        axios.get("http://localhost:5000/")
            .then((res) => {
                setMessages(res.data)
            })
           
        return () => {
            socket.disconnect();
        }
    }, [])
    useEffect(() => {

        socket.on('msgSent', (data) => {
            setMessages([...messages, data])
        })
    }, [messages])
    const renderMessages = () => {
        return (
            messages.map( (message) => {
                return (
                    <Messages key={message._id} sender={message.sender.firstName.concat(message.sender.lastName)}
                              messageText={message.messageText}
                               />
                )
            })
        )
    }
    const sendMessage = (e) => {
        e.preventDefault()
        const message = {
            messageText: input,
        }
        axios.post("http://localhost:5000/", message)
            .then(res => {
                setMessages([...messages, res.data])
                socket.emit('msgSent', res.data)
            })         
    }
    return (       
                {renderMessages()}
                <form onSubmit={(e) => sendMessage(e)}>
                    <input type="text" placeholder="type a message" value={input}
                           onChange={(e) => setInput(e.target.value)}/>
                    <button type="submit"></button>
                </form>                
    );
}
export default Chat;

这是我的服务器端代码

const express = require("express")
const app = express()
const mongoose = require('mongoose')
const homeRoot = require("./Routes/homeRoot")
const http = require('http').Server(app);
const io = require('socket.io')(http)    
//listening
http.listen(5000, () => {
    console.log("hello from server")
})

//Routes
app.use("/", homeRoot)

//socket
io.on('connection', (socket) => {
    socket.on('msgSent',(data)=>{
        socket.broadcast.emit('msgSent',data)
    })
});

//Db connection
mongoose.connect("mongodb+srv://@cluster0.agiog.mongodb.net/myFirstDatabase?retryWrites=true&w=majority")

这是我获取消息的 homeRoot

const express = require("express")
const Router = express.Router()
const Messages = require("../Models/messageModel")
const Users = require("../Models/userModel")
const auth = require("../auth")
Router.get("/",auth,async(req,res)=>{
    try {
        const messages = await Messages.find().populate("sender","firstName lastName")
        res.status(200).send(messages)
    }catch (e) {
        res.status(404).send("messages not found")
    }
})

Router.post("/",auth,async(req,res)=>{

    const user = await Users.findOne({_id:req.userId})
    const message = new Messages({...req.body,sender:user})
    try {
        const savedMessage = await message.save()
        res.status(201).send(savedMessage)

    }catch (e) {
        res.status(404).send("error occured")
    }
})

module.exports = Router
useEffect(() => {
   socket.on('msgSent', (data) => {
   setMessages([...messages, data])
  })

return{
 socket.off("msgSent");
 }
}, [messages])

你需要在上面的例子中像这样使用 socket.off()。

Yadnesh 给出的答案有一点小问题。让我解释一下。

首先让我们看看为什么您的代码有问题?


您已将 socket.on 侦听器直接放置在您的组件内,而不是将其放置在任何钩子内。这意味着每次您的组件重新渲染时,都会创建一个新的 socket.on 侦听器。
  • 您的组件已挂载 -> 您将拥有一个 socket.on 侦听器
  • 您收到第一条消息 -> 之前的监听器仍然存在,但由于组件重新渲染,将创建一个新的监听器
  • 您收到第二条消息 -> 前两个侦听器加起来现在总共有 4 个侦听器

这种模式还在继续..

您可能想使用下面给出的代码,但这是错误的方法。

useEffect(() => {
  socket.on('msgSent', (data) => {
   setMessages([...messages, data])
  });

  return () => {
    socket.off("msgSend");
  };
}, [messages]);

使用 [messageList] 作为 useEffect 的第二个参数的问题 ??

问题是您不想在每次更新 messageList 时都创建一个新的 socket.io 侦听器。在组件的生命周期中,您应该始终只为一个事件创建一个侦听器。因为一旦 socket.on 侦听器被创建,它就不会在侦听该事件的第一次发生后被销毁,它只会再次侦听该事件。

所以这样的监听器必须创建一次。在您的情况下,它应该在组件安装时创建。

因此使用 [],空列表作为 useEffect 的第二个参数。这个钩子相当于 componentDidMount.

正确的代码

useEffect(() => {
  socket.on('msgSent', (data) => {
   setMessages([...messages, data])
  });

  return () => {
    socket.off("msgSend");
  };
}, []);