使用 React 和 Socket.io 的聊天应用程序在发送太多消息后挂起

Posted

技术标签:

【中文标题】使用 React 和 Socket.io 的聊天应用程序在发送太多消息后挂起【英文标题】:Chat App Using React and Socket.io Hanging After Sending Too Many Messages 【发布时间】:2021-01-13 03:12:27 【问题描述】:

我的基本聊天应用程序在发送大约 7 条消息后变得非常缓慢,尽管它在只发送几条消息时确实有效。

我在返回的组件中放置了一个打印语句。我注意到每次提交聊天消息时,打印语句的调用次数都是以前的两倍。我假设我的应用程序变慢了,因为我发送更多消息时必须以指数方式重新渲染 DOM。

有人知道为什么会这样吗?

这是我的 React 应用程序

import React from 'react';
import io from "socket.io-client";
let socket = io.connect("localhost:8080");

function App() 
  const [curMessage, setMessage] = React.useState(name: "", message: "");
  const [messageList, setList] = React.useState([]);

  socket.on("receiveMsg", message => 
    setList([...messageList, message]);
  );

  function handleTyping(e) 
    let target = e.target;
    if (target.id === "name") 
      setMessage(...curMessage, name: target.value);
     else 
      setMessage(...curMessage, message: target.value);
    
  

  function handleSubmit(e) 
    e.preventDefault();
    setList([...messageList, curMessage]);
    setMessage(name: "", message: "");
    socket.emit("message", curMessage);
  



  return (
    <div>
      <form onSubmit=handleSubmit>
        <input id="name" onChange=handleTyping value=curMessage.name></input>
        <input id="message" onChange=handleTyping value=curMessage.message></input>
        <button>Send Message</button>
      </form>
      <ul>
        messageList.map(msg => 
          return <li> msg.name msg.message </li>
        )
        console.log("test")
      </ul>
    </div>
  );


export default App;

这是我的服务器端代码(虽然我认为这段代码没问题)

const express = require("express");
const app = express();
const http = require("http").createServer(app);
const io = require("socket.io")(http);

http.listen(8080, () => console.log("Connected to Server"));

io.on("connection", socket => 
  console.log("User has connected " + socket.id);
  socket.on("message", data => 
    socket.broadcast.emit("receiveMsg", data);
  );
);

【问题讨论】:

【参考方案1】:

尝试将您的socket.on(..) 放入 useEffect 中。也许在渲染中执行 setState 是导致速度变慢的原因。

useEffect(() => 
  socket.on("receiveMsg", (message) => 
    setList([...messageList, message]);
  );

  return () => 
    socket.off("receiveMsg");
  ;
, [messageList]);

【讨论】:

【参考方案2】:

Chanandrei 给出的答案有一个小陷阱。让我解释一下。

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

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

这种模式还在继续..

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

useEffect(() => 
  socket.on("receiveMsg", (message) => 
    setList([...messageList, message]);
  );

  return () => 
    socket.off("receiveMsg");
  ;
, [messageList]);

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

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

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

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

正确的代码

useEffect(() => 
  socket.on("receiveMsg", (message) => 
    setList([...messageList, message]);
  );

  return () => 
    socket.off("receiveMsg");
  ;
, []);

【讨论】:

【参考方案3】:

使用此代码,您将收到一条警告,提示您需要 messageList 作为 useEffect 中的依赖项

useEffect(() => 
  socket.on("receiveMsg", (message) => 
    setList([...messageList, message]);
  );

  return () => 
    socket.off("receiveMsg");
  ;
, []);

你只需要像这样进行功能更新

setList((prevMsgs) => [...prevMsgs, message]);

【讨论】:

即使有关于依赖的警告,如果您不希望依赖的更改触发重新渲染,有时也可以将其留空

以上是关于使用 React 和 Socket.io 的聊天应用程序在发送太多消息后挂起的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 React Native 将 Socket io 连接到 https

Node.js Socket.IO 聊天应用集群服务器

反应的socket.io聊天问题

带有 Socket.io 和 Node 的动态房间

Socket.io - React:如何在 React 中检索保存的 socket.io 数据(MongoDB)?

React Native、NodeJS、Socket.io