使用 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