React 18中Socket.io客户端消息重复接收与useEffect执行两次问题
原因分析
React 18的StrictMode在开发环境中会重复执行组件的挂载逻辑(包括useEffect的回调和清理函数),而你当前的Socket.io事件清理逻辑存在错误:socket.off需要传入与绑定阶段完全相同的回调函数才能移除对应的监听器,但你在清理时定义了一个新的匿名函数,导致旧的监听器根本没被清理。每次组件"挂载"(包括StrictMode的二次执行)都会新增一个监听器,最终导致收到两次消息。
解决方案
有两种可行的修复方式,都能在React 18 StrictMode下正常工作:
方案1:复用回调函数(精准移除)
将监听的回调函数提取为独立变量,确保绑定和移除使用同一个函数:
import React, { useEffect } from "react"; function Chat({ socket, username, room }) { // 提取独立的回调函数 const handleReceiveMessage = (data) => { console.log(data); }; useEffect(() => { socket.on("receive_message", handleReceiveMessage); // 清理时传入同一个函数 return () => { socket.off("receive_message", handleReceiveMessage); }; }, [socket]); return ( <> <input type="text" placeholder="John..." onChange={(event) => { console.log(event.target.value); }} onKeyPress={(event) => { if (event.key === "Enter") { socket.emit("send_message", { room: room, author: username, message: event.target.value, }); } }} /> </> ); } export default Chat;
方案2:移除该事件的所有监听器(简洁高效)
如果该事件下只需要一个监听器,可以直接调用socket.off("receive_message")移除该事件的所有监听器:
import React, { useEffect } from "react"; function Chat({ socket, username, room }) { useEffect(() => { socket.on("receive_message", (data) => { console.log(data); }); return () => { // 直接移除receive_message事件的所有监听器 socket.off("receive_message"); }; }, [socket]); return ( <> <input type="text" placeholder="John..." onChange={(event) => { console.log(event.target.value); }} onKeyPress={(event) => { if (event.key === "Enter") { socket.emit("send_message", { room: room, author: username, message: event.target.value, }); } }} /> </> ); } export default Chat;
两种方案都能解决重复监听的问题,方案1适合需要精准管理多个同事件监听器的场景,方案2适合简单场景下的快速修复。
内容的提问来源于stack exchange,提问作者Talha Amjad
相关产品推荐
相关产品推荐

