繁体   English   中英

Socket.io 和 React:从服务器接收数据并在组件中设置 state 的正确方法?

[英]Socket.io and React: Proper way too receive data from server and set state in a component?

尝试运行一个基本的聊天应用程序,但在发送消息时遇到过度重新呈现的问题。 这是客户端的适用代码:

const [chatMessages, setChatMessages] = useState([]);
const sendChat = (e) => {
    socket.emit("sendMessage", e.target.elements.message.value);
}

useEffect(() => {
  socket.on("receiveMessage", (chatMessage) => {
    setChatMessages([...chatMessages, chatMessage]);
      console.log(chatMessages);
  });
}, [chatMessages]);
return (
    {chatMessages.map((message) => <p>{message}</p>)}
)

然后,在服务器上:

io.on("connection", (socket) => {
    socket.on("sendMessage", (chatMessage) => {
        console.log("message sent");
        io.to(roomId).emit("receiveMessage", chatMessage);
    });
}

当我这样做时,消息已成功发送和接收,但会导致它发生很多次(控制台):

[]
[]
[{...}]
[{...}]
(2) [{...}, {...}]

在第三条消息中,这是记录的内容。 到第六条或第七条消息时,整个页面都停止了,因为它记录了大约 100 次。

我尝试了以下方法:

  1. useEffect()中有一个空的依赖数组。 这确实修复了重新渲染,但引入了一个新问题。 最新消息是唯一保存并替换最后一条消息的消息,因此您一次只能看到一条消息。

  2. 将其全部从useEffect()中取出。 这只会使问题恶化,并导致每条消息的重新呈现次数更多。

任何帮助,将不胜感激。 谢谢!

问题

chatMessages state 更新但不清除它们时,您正在创建套接字事件处理程序。 如果您编辑您的代码或组件重新呈现等......然后添加另一个套接字事件处理程序。 多个处理程序将开始堆叠并排队多个意外的 state 更新。

此外,由于 React state 更新是异步处理的,您无法在更新排队后立即记录 state 并期望看到更新后的 state。为此使用单独的useEffect挂钩。

解决方案

  1. 添加useEffect清理 function 以删除事件处理程序,并在处理程序回调中重新包含更新的chatMessages state 数组。

     useEffect(() => { const handler = (chatMessage) => { setChatMessages([...chatMessages, chatMessage]); } socket.on("receiveMessage", handler); return () => socket.off("receiveMessage", handler); }, [chatMessages]);
  2. 添加一个useEffect清理 function,删除依赖项以便效果在组件安装时运行一次,并使用功能性 state 更新从回调附件中的先前 state 而不是初始 state 正确更新。

     useEffect(() => { const handler = (chatMessage) => { setChatMessages(chatMessages => [...chatMessages, chatMessage]); } socket.on("receiveMessage", handler); return () => socket.off("receiveMessage", handler); }, []);

在这两者之间,第二个选项是更优的解决方案,但你选择哪个是你的决定。

要记录聊天消息chatMessages更新:

useEffect(() => {
  console.log(chatMessages);
}, [chatMessages]);

由于您依赖于chatMessages ,每次chatMessages更改时,它都会创建一个新的侦听器。 这就是为什么当收到更多消息时它变得越来越慢。

你可以做两件事:

  1. 您在useEffect方法中本地维护chatMessages 您可以展开该数组,然后使用展开的数组调用setChatMessages 执行此操作时,您可以删除chatMessagesuseEffect的依赖性,并且仍然拥有所有消息。 作为一种好的做法,您应该返回一个 function,它将在组件卸载时删除事件侦听器。

 const [chatMessages, setChatMessages] = useState([]); const sendChat = (e) => { socket.emit("sendMessage", e.target.elements.message.value); } useEffect(() => { let localMessages = []; const callback = (chatMessage) => { localMessages = [...localMessages, chatMessage]; setChatMessages(localMessages); console.log(localMessages); }; socket.on("receiveMessage", callback); return () => { socket.off("receiveMessage", callback); } }, []); return ( {chatMessages.map((message) => <p>{message}</p>)} )

  1. 您可能可以使用useRef来存储值。 但是,当值更改时,这不会触发 UI 的重新呈现,这可能不是您想要的。 所以这可能不是一个好的选择。

暂无
暂无

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

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