From 2fcc3412134563660880a3cf9e05038e84e3fda0 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Wed, 26 Nov 2025 10:55:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/contexts/NotificationContext.js | 39 +++++++++++++++++++---------- src/services/socketService.js | 16 +++++++++--- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/contexts/NotificationContext.js b/src/contexts/NotificationContext.js index 0058c104..f97a0c7e 100644 --- a/src/contexts/NotificationContext.js +++ b/src/contexts/NotificationContext.js @@ -27,6 +27,9 @@ const CONNECTION_STATUS = { RECONNECTED: 'reconnected', // 重连成功(显示2秒后自动变回 CONNECTED) }; +// ⚡ 模块级变量:防止 React Strict Mode 导致的重复初始化 +let socketInitialized = false; + // 创建通知上下文 const NotificationContext = createContext(); @@ -651,14 +654,21 @@ export const NotificationProvider = ({ children }) => { // ========== 连接到 Socket 服务(⚡ 异步初始化,不阻塞首屏) ========== useEffect(() => { + // ⚡ 防止 React Strict Mode 导致的重复初始化 + if (socketInitialized) { + logger.debug('NotificationContext', 'Socket 已初始化,跳过重复执行(Strict Mode 保护)'); + return; + } + let cleanupCalled = false; let idleCallbackId; let timeoutId; // ⚡ Socket 初始化函数(将在浏览器空闲时执行) const initSocketConnection = () => { - if (cleanupCalled) return; // 防止组件卸载后执行 + if (cleanupCalled || socketInitialized) return; // 防止组件卸载后执行或重复初始化 + socketInitialized = true; // 标记已初始化 logger.info('NotificationContext', '初始化 Socket 连接(异步执行,不阻塞首屏)'); // ========== 监听连接成功(首次连接 + 重连) ========== @@ -690,19 +700,22 @@ export const NotificationProvider = ({ children }) => { } // ⚡ 重连后只需重新订阅,不需要重新注册监听器 - logger.info('NotificationContext', '重新订阅事件推送'); + // 使用 setTimeout(0) 确保 socketService 内部状态已同步 + setTimeout(() => { + logger.info('NotificationContext', '重新订阅事件推送'); - if (socket.subscribeToEvents) { - socket.subscribeToEvents({ - eventType: 'all', - importance: 'all', - onSubscribed: (data) => { - logger.info('NotificationContext', '订阅成功', data); - }, - }); - } else { - logger.error('NotificationContext', 'socket.subscribeToEvents 方法不可用'); - } + if (socket.subscribeToEvents) { + socket.subscribeToEvents({ + eventType: 'all', + importance: 'all', + onSubscribed: (data) => { + logger.info('NotificationContext', '订阅成功', data); + }, + }); + } else { + logger.error('NotificationContext', 'socket.subscribeToEvents 方法不可用'); + } + }, 0); }); // ========== 监听断开连接 ========== diff --git a/src/services/socketService.js b/src/services/socketService.js index 874c4b96..6df22196 100644 --- a/src/services/socketService.js +++ b/src/services/socketService.js @@ -329,10 +329,18 @@ class SocketService { onSubscribed, } = options; - if (!this.socket || !this.connected) { - logger.warn('socketService', 'Cannot subscribe: socket not connected'); - // 自动连接 - this.connect(); + // ⚡ 改进状态检查:同时检查 this.connected 和 socket.connected + // 解决 connect 回调中 this.connected 尚未更新的竞争条件 + const isReady = this.socket && (this.socket.connected || this.connected); + + if (!isReady) { + logger.debug('socketService', 'Socket 尚未就绪,等待连接后订阅'); + + if (!this.socket) { + // 自动连接 + this.connect(); + } + // 等待连接成功后再订阅 this.socket.once('connect', () => { this._doSubscribe(eventType, importance, onNewEvent, onSubscribed);