- axiosConfig: 忽略 CanceledError 错误日志(组件卸载时的正常行为) - socketService: 首次连接失败使用 warn 级别,后续重试使用 debug 级别 - useEventData: 添加防御性检查,防止 pagination 为 undefined 时崩溃 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
127 lines
4.0 KiB
JavaScript
127 lines
4.0 KiB
JavaScript
// src/views/Community/hooks/useEventData.js
|
||
// 事件数据加载逻辑 Hook
|
||
|
||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||
import { debounce } from 'lodash';
|
||
import { eventService } from '../../../services/eventService';
|
||
import { logger } from '../../../utils/logger';
|
||
|
||
/**
|
||
* 事件数据加载 Hook
|
||
* @param {Object} filters - 筛选条件
|
||
* @param {number} pageSize - 每页数量
|
||
* @returns {Object} 事件数据和加载状态
|
||
*/
|
||
export const useEventData = (filters, pageSize = 10) => {
|
||
const [events, setEvents] = useState([]);
|
||
const [pagination, setPagination] = useState({
|
||
current: 1,
|
||
pageSize: pageSize,
|
||
total: 0
|
||
});
|
||
const [loading, setLoading] = useState(false);
|
||
const [lastUpdateTime, setLastUpdateTime] = useState(new Date());
|
||
|
||
// 加载事件列表
|
||
// ✅ 修复闭包陷阱: 接受 currentFilters 参数,避免使用闭包中的旧 filters
|
||
const loadEvents = useCallback(async (page = 1, currentFilters = null) => {
|
||
// 使用传入的 currentFilters 或回退到闭包中的 filters
|
||
const filtersToUse = currentFilters || filters;
|
||
|
||
const requestParams = {
|
||
...filtersToUse,
|
||
page,
|
||
per_page: pagination.pageSize
|
||
};
|
||
|
||
logger.debug('useEventData', '📡 【准备发起API请求】loadEvents 被调用', {
|
||
page,
|
||
currentFilters,
|
||
filtersToUse,
|
||
requestParams
|
||
});
|
||
|
||
setLoading(true);
|
||
|
||
try {
|
||
logger.debug('useEventData', '🌐 正在调用 eventService.getEvents', { requestParams });
|
||
const response = await eventService.getEvents(requestParams);
|
||
logger.debug('useEventData', '✅ API响应成功', {
|
||
success: response.success,
|
||
eventCount: response.data?.events?.length,
|
||
total: response.data?.pagination?.total
|
||
});
|
||
|
||
if (response.success && response.data) {
|
||
const events = response.data.events || [];
|
||
const paginationData = response.data.pagination || {};
|
||
|
||
setEvents(events);
|
||
setPagination({
|
||
current: paginationData.page || page,
|
||
pageSize: paginationData.per_page || pagination.pageSize,
|
||
total: paginationData.total || 0
|
||
});
|
||
setLastUpdateTime(new Date());
|
||
|
||
logger.debug('useEventData', 'loadEvents 成功', {
|
||
count: events.length,
|
||
total: paginationData.total || 0
|
||
});
|
||
}
|
||
} catch (error) {
|
||
logger.error('useEventData', '❌ loadEvents 失败', error, {
|
||
page,
|
||
filtersToUse
|
||
});
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [filters, pagination.pageSize]);
|
||
|
||
// 创建防抖的 loadEvents 函数(500ms 防抖延迟)
|
||
// ✅ 修复闭包陷阱: 防抖函数接受 filters 参数并传递给 loadEvents
|
||
const debouncedLoadEvents = useRef(
|
||
debounce((page, filters) => {
|
||
logger.debug('useEventData', '⏱️ 【防抖延迟500ms结束】即将执行 loadEvents', {
|
||
page,
|
||
filters
|
||
});
|
||
loadEvents(page, filters);
|
||
}, 500)
|
||
).current;
|
||
|
||
// 监听 filters 变化,自动加载数据
|
||
// 防抖优化:用户快速切换筛选条件时,只执行最后一次请求
|
||
useEffect(() => {
|
||
logger.debug('useEventData', '🔔 【filters变化触发useEffect】完整filters对象:', filters);
|
||
logger.debug('useEventData', '详细参数:', {
|
||
page: filters.page || 1,
|
||
sort: filters.sort,
|
||
importance: filters.importance,
|
||
date_range: filters.date_range,
|
||
q: filters.q,
|
||
industry_code: filters.industry_code,
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
|
||
// ✅ 使用防抖加载事件,将当前 filters 传递给防抖函数
|
||
logger.debug('useEventData', '⏰ 启动防抖计时器(500ms),传递最新filters');
|
||
debouncedLoadEvents(filters.page || 1, filters);
|
||
|
||
// 组件卸载时取消防抖
|
||
return () => {
|
||
debouncedLoadEvents.cancel();
|
||
};
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [filters]); // 监听 filters 状态变化
|
||
|
||
return {
|
||
events,
|
||
pagination,
|
||
loading,
|
||
lastUpdateTime,
|
||
loadEvents
|
||
};
|
||
};
|