241 lines
9.4 KiB
JavaScript
241 lines
9.4 KiB
JavaScript
// src/hooks/useEventNotifications.js
|
||
/**
|
||
* React Hook:用于在组件中订阅事件推送通知
|
||
*
|
||
* 使用示例:
|
||
* ```jsx
|
||
* import { useEventNotifications } from 'hooks/useEventNotifications';
|
||
*
|
||
* function MyComponent() {
|
||
* const { newEvent, isConnected } = useEventNotifications({
|
||
* eventType: 'all',
|
||
* importance: 'all',
|
||
* onNewEvent: (event) => {
|
||
* console.log('收到新事件:', event);
|
||
* // 显示通知...
|
||
* }
|
||
* });
|
||
*
|
||
* return <div>...</div>;
|
||
* }
|
||
* ```
|
||
*/
|
||
|
||
import { useEffect, useState, useRef } from 'react';
|
||
import { socketService } from '../services/socketService';
|
||
import { logger } from '../utils/logger';
|
||
|
||
export const useEventNotifications = (options = {}) => {
|
||
const {
|
||
eventType = 'all',
|
||
importance = 'all',
|
||
enabled = true,
|
||
onNewEvent,
|
||
} = options;
|
||
|
||
const [isConnected, setIsConnected] = useState(false);
|
||
const [newEvent, setNewEvent] = useState(null);
|
||
const [error, setError] = useState(null);
|
||
const unsubscribeRef = useRef(null);
|
||
|
||
// 使用 ref 存储 onNewEvent 回调,避免因回调函数引用改变导致重新连接
|
||
const onNewEventRef = useRef(onNewEvent);
|
||
|
||
// 每次 onNewEvent 改变时更新 ref
|
||
useEffect(() => {
|
||
onNewEventRef.current = onNewEvent;
|
||
}, [onNewEvent]);
|
||
|
||
useEffect(() => {
|
||
console.log('[useEventNotifications DEBUG] ========== useEffect 执行 ==========');
|
||
console.log('[useEventNotifications DEBUG] enabled:', enabled);
|
||
console.log('[useEventNotifications DEBUG] eventType:', eventType);
|
||
console.log('[useEventNotifications DEBUG] importance:', importance);
|
||
|
||
// 如果禁用,则不订阅
|
||
if (!enabled) {
|
||
console.log('[useEventNotifications DEBUG] ⚠️ 订阅已禁用,跳过');
|
||
return;
|
||
}
|
||
|
||
// 连接状态监听
|
||
const handleConnect = () => {
|
||
console.log('[useEventNotifications DEBUG] ✓ WebSocket 已连接');
|
||
logger.info('useEventNotifications', 'WebSocket connected');
|
||
setIsConnected(true);
|
||
setError(null);
|
||
};
|
||
|
||
const handleDisconnect = () => {
|
||
console.log('[useEventNotifications DEBUG] ⚠️ WebSocket 已断开');
|
||
logger.warn('useEventNotifications', 'WebSocket disconnected');
|
||
setIsConnected(false);
|
||
};
|
||
|
||
const handleConnectError = (err) => {
|
||
console.error('[useEventNotifications ERROR] WebSocket 连接错误:', err);
|
||
logger.error('useEventNotifications', 'WebSocket connect error', err);
|
||
setError(err);
|
||
setIsConnected(false);
|
||
};
|
||
|
||
// 监听连接事件(必须在connect之前设置,否则可能错过事件)
|
||
socketService.on('connect', handleConnect);
|
||
socketService.on('disconnect', handleDisconnect);
|
||
socketService.on('connect_error', handleConnectError);
|
||
|
||
// 连接 WebSocket
|
||
console.log('[useEventNotifications DEBUG] 准备连接 WebSocket...');
|
||
logger.info('useEventNotifications', 'Initializing WebSocket connection');
|
||
|
||
// 先检查是否已经连接
|
||
const alreadyConnected = socketService.isConnected();
|
||
console.log('[useEventNotifications DEBUG] 当前连接状态:', alreadyConnected);
|
||
logger.info('useEventNotifications', 'Pre-connection check', { isConnected: alreadyConnected });
|
||
|
||
if (alreadyConnected) {
|
||
// 如果已经连接,直接更新状态
|
||
console.log('[useEventNotifications DEBUG] Socket已连接,直接更新状态');
|
||
logger.info('useEventNotifications', 'Socket already connected, updating state immediately');
|
||
setIsConnected(true);
|
||
// 验证状态更新
|
||
setTimeout(() => {
|
||
console.log('[useEventNotifications DEBUG] 1秒后验证状态更新 - isConnected应该为true');
|
||
}, 1000);
|
||
} else {
|
||
// 否则建立新连接
|
||
console.log('[useEventNotifications DEBUG] Socket未连接,开始连接...');
|
||
socketService.connect();
|
||
}
|
||
|
||
// 新事件处理函数 - 使用 ref 中的回调
|
||
const handleNewEvent = (eventData) => {
|
||
console.log('\n[useEventNotifications DEBUG] ========== Hook 收到新事件 ==========');
|
||
console.log('[useEventNotifications DEBUG] 事件数据:', eventData);
|
||
console.log('[useEventNotifications DEBUG] 事件 ID:', eventData?.id);
|
||
console.log('[useEventNotifications DEBUG] 事件标题:', eventData?.title);
|
||
|
||
console.log('[useEventNotifications DEBUG] 设置 newEvent 状态');
|
||
setNewEvent(eventData);
|
||
console.log('[useEventNotifications DEBUG] ✓ newEvent 状态已更新');
|
||
|
||
// 调用外部回调(从 ref 中获取最新的回调)
|
||
if (onNewEventRef.current) {
|
||
console.log('[useEventNotifications DEBUG] 准备调用外部 onNewEvent 回调');
|
||
onNewEventRef.current(eventData);
|
||
console.log('[useEventNotifications DEBUG] ✓ 外部 onNewEvent 回调已调用');
|
||
} else {
|
||
console.log('[useEventNotifications DEBUG] ⚠️ 没有外部 onNewEvent 回调');
|
||
}
|
||
|
||
console.log('[useEventNotifications DEBUG] ========== Hook 事件处理完成 ==========\n');
|
||
};
|
||
|
||
// 订阅事件推送
|
||
console.log('\n[useEventNotifications DEBUG] ========== 开始订阅事件 ==========');
|
||
console.log('[useEventNotifications DEBUG] eventType:', eventType);
|
||
console.log('[useEventNotifications DEBUG] importance:', importance);
|
||
console.log('[useEventNotifications DEBUG] enabled:', enabled);
|
||
|
||
socketService.subscribeToEvents({
|
||
eventType,
|
||
importance,
|
||
onNewEvent: handleNewEvent,
|
||
onSubscribed: (data) => {
|
||
console.log('\n[useEventNotifications DEBUG] ========== 订阅成功回调 ==========');
|
||
console.log('[useEventNotifications DEBUG] 订阅数据:', data);
|
||
console.log('[useEventNotifications DEBUG] ========== 订阅成功处理完成 ==========\n');
|
||
},
|
||
});
|
||
console.log('[useEventNotifications DEBUG] ========== 订阅请求已发送 ==========\n');
|
||
|
||
// 保存取消订阅函数
|
||
unsubscribeRef.current = () => {
|
||
socketService.unsubscribeFromEvents({ eventType });
|
||
};
|
||
|
||
// 组件卸载时清理
|
||
return () => {
|
||
console.log('\n[useEventNotifications DEBUG] ========== 清理 WebSocket 订阅 ==========');
|
||
|
||
// 取消订阅
|
||
if (unsubscribeRef.current) {
|
||
console.log('[useEventNotifications DEBUG] 取消订阅...');
|
||
unsubscribeRef.current();
|
||
}
|
||
|
||
// 移除监听器
|
||
console.log('[useEventNotifications DEBUG] 移除事件监听器...');
|
||
socketService.off('connect', handleConnect);
|
||
socketService.off('disconnect', handleDisconnect);
|
||
socketService.off('connect_error', handleConnectError);
|
||
|
||
// 注意:不断开连接!
|
||
// socketService 是全局单例,可能被多个组件使用
|
||
// 断开连接会影响其他组件,所以只移除监听器和取消订阅
|
||
console.log('[useEventNotifications DEBUG] ⚠️ 保持连接(socketService是全局单例)');
|
||
// socketService.disconnect(); // 注释掉,不主动断开
|
||
|
||
console.log('[useEventNotifications DEBUG] ========== 清理完成 ==========\n');
|
||
};
|
||
}, [eventType, importance, enabled]); // 移除 onNewEvent 依赖
|
||
|
||
// 监控 isConnected 状态变化(调试用)
|
||
useEffect(() => {
|
||
console.log('[useEventNotifications DEBUG] ========== isConnected 状态变化 ==========');
|
||
console.log('[useEventNotifications DEBUG] isConnected:', isConnected);
|
||
console.log('[useEventNotifications DEBUG] ===========================================');
|
||
}, [isConnected]);
|
||
|
||
console.log('[useEventNotifications DEBUG] Hook返回值 - isConnected:', isConnected);
|
||
|
||
return {
|
||
newEvent, // 最新收到的事件
|
||
isConnected, // WebSocket 连接状态
|
||
error, // 错误信息
|
||
clearNewEvent: () => setNewEvent(null), // 清除新事件状态
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 简化版 Hook:只订阅所有事件
|
||
*/
|
||
export const useAllEventNotifications = (onNewEvent) => {
|
||
return useEventNotifications({
|
||
eventType: 'all',
|
||
importance: 'all',
|
||
onNewEvent,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Hook:订阅重要事件(S 和 A 级)
|
||
*/
|
||
export const useImportantEventNotifications = (onNewEvent) => {
|
||
const [importantEvents, setImportantEvents] = useState([]);
|
||
|
||
const handleEvent = (event) => {
|
||
// 只处理 S 和 A 级事件
|
||
if (event.importance === 'S' || event.importance === 'A') {
|
||
setImportantEvents(prev => [event, ...prev].slice(0, 10)); // 最多保留 10 个
|
||
if (onNewEvent) {
|
||
onNewEvent(event);
|
||
}
|
||
}
|
||
};
|
||
|
||
const result = useEventNotifications({
|
||
eventType: 'all',
|
||
importance: 'all',
|
||
onNewEvent: handleEvent,
|
||
});
|
||
|
||
return {
|
||
...result,
|
||
importantEvents,
|
||
clearImportantEvents: () => setImportantEvents([]),
|
||
};
|
||
};
|
||
|
||
export default useEventNotifications;
|