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

我的基本聊天应用程序在发送大约 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);
  });
});

【问题讨论】:

    标签: node.js reactjs sockets socket.io frontend


    【解决方案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]);
        

        【讨论】:

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