Socket重复调用致会话列表UI重复更新问题求助
问题解决:实时消息重复显示后合并的问题
问题根源
- Socket事件重复绑定:
receivePersonalMessage的useCallback依赖为空数组,但组件重渲染时该函数会重新创建,导致useEffect里的socket.on重复绑定,同一条消息触发两次回调。 - 状态操作不规范:直接引用
listData进行修改(const contactList: any = listData)会污染原状态;当findIndex返回-1(会话不在当前列表)时,splice(-1,1)会错误删除列表最后一项。 - 重复更新状态:回调里先手动修改状态,又调用
handleListMyContact拉取后端列表,两次状态更新导致界面先显示手动添加的消息,再被接口返回的列表覆盖。
修复方案
核心修改点
- 用函数式状态更新处理列表,避免直接操作原状态数组
- 修正Socket事件绑定逻辑,防止重复监听
- 移除重复的状态更新操作,优先用实时状态更新保证体验,仅在页面焦点恢复时同步后端数据
修复后代码
// 获取会话列表:添加authToken依赖,确保token变化时重新请求 const handleListMyContact = useCallback(async () => { const data = await getConversation(authToken); if (data) { setListData(data.info); } else { console.log("loading data..."); } }, [authToken]); // 页面焦点恢复时同步会话列表 useEffect(() => { if (focus) { handleListMyContact(); } }, [handleListMyContact, focus]); // 接收实时消息:用函数式更新处理状态,避免引用污染 const receivePersonalMessage = useCallback((data: any) => { console.log("data from emit", data); setListData(prevList => { const newList = [...prevList]; const targetIdx = newList.findIndex(item => item.conversation_id === data.conversation_id); // 若会话已存在,先移除原项 if (targetIdx !== -1) { newList.splice(targetIdx, 1); } // 将新消息会话置顶 newList.unshift(data); return newList; }); }, []); // Socket事件绑定:用局部变量保存回调,确保解绑与绑定的是同一个函数 useEffect(() => { const messageHandler = receivePersonalMessage; socket.on("new_conversation_message", messageHandler); return () => { socket.off("new_conversation_message", messageHandler); }; }, [socket]);
修复说明
- 函数式状态更新:通过
setListData(prevList => ...)直接基于上一次状态修改,避免直接引用原数组导致的状态污染问题。 - Socket事件绑定优化:将回调赋值给局部变量,确保每次解绑的是同一个函数,同时减少不必要的依赖项,防止重复绑定。
- 移除重复更新:手动更新状态已经能实时显示最新消息,不需要立即调用接口拉取,保留页面焦点时的同步逻辑即可,减少网络请求和界面抖动。
内容的提问来源于stack exchange,提问作者Milan_Dignizant
相关产品推荐
相关产品推荐

