ReactJs组件useEffect内无法监听socket事件的问题
问题根因
监听逻辑写入useEffect后不生效,是代码写法存在几处和React运行机制、socket.io使用规范冲突的问题导致的:
- 绑定socket逻辑的
useEffect没有传入依赖数组,组件每次重渲染都会重新执行effect逻辑,重复创建新的socket连接、重复绑定同名事件,旧连接没有关闭、旧监听没有移除,会出现事件多次触发、回调逻辑执行混乱的问题。 - 你把消息存在组件外部的全局普通数组
data里,socket收到消息修改这个数组时,不会触发React的重渲染流程,哪怕数据已经更新,视图也不会同步刷新,看起来就像监听没生效。你之前把监听写在组件外部觉得“正常运行”,只是因为模块加载时外部代码仅执行一次不会重复创建连接,但修改外部变量不触发重渲染的问题依然存在,只是其他逻辑触发重渲染时刚好把新数据带出来了,属于偶现的正常,不是写法正确。 - 组件每次渲染都会直接执行
data.push(sendMessage),没有做任何去重或依赖判断,会重复往数组里插入重复消息,导致列表渲染冗余。 - 在
useEffect里创建的socket实例没有做卸载清理,组件销毁后监听和连接依然残留,会造成内存泄漏。
修复后可直接运行的代码
import { useEffect, useState } from "react"; import ChatBubble from "../components/Chatbubblebot"; import { io } from 'socket.io-client'; // socket实例提到组件外部,全局复用同一个连接,避免重复创建 const socket = io("http://localhost:5000", { autoConnect: false }); function Messagelist({ sendMessage }) { // 用React官方state管理消息列表,数据变更时自动触发视图更新 const [messageList, setMessageList] = useState([]); useEffect(() => { // 组件挂载时建立连接 socket.connect(); // 抽离事件回调,方便后续卸载时精准移除监听 const handleAdminMessage = (payload) => { setMessageList(prevList => [...prevList, {who:"him", message:payload}]); }; // 绑定事件监听 socket.on("admin", handleAdminMessage); // 组件卸载时执行清理:移除监听、断开连接,避免内存泄漏 return () => { socket.off("admin", handleAdminMessage); socket.disconnect(); }; }, []); // 空依赖数组,保证这段逻辑仅在组件首次挂载、最终卸载时各执行一次 // 同步父组件传入的新消息 useEffect(() => { if (sendMessage) { setMessageList(prevList => [...prevList, sendMessage]); } }, [sendMessage]); return ( <ul> {messageList.map((item, index) => ( <ChatBubble // 有服务端返回的唯一消息ID时优先用ID当key,避免列表渲染错位 key={index} who={item.who} message={item.message} /> ))} </ul> ); } export default Messagelist;
关键修改点说明
- 所有消息数据统一用
useState管理,所有对消息列表的修改都通过setMessageList执行,从根源解决数据变了视图不更新的问题。 - 给socket相关的
useEffect传入空依赖数组,彻底解决重复创建连接、重复绑定监听的问题。 - 补充effect清理逻辑,组件卸载时主动移除监听、断开连接,避免无效内存占用。
- 所有带副作用的逻辑(比如接收新消息、同步外部传入的props)全部放到
useEffect中,根据依赖项触发执行,不在组件渲染函数直接执行数据修改操作,避免重复插入冗余数据。
内容的提问来源于stack exchange,提问作者Karthik
相关产品推荐
相关产品推荐

