feat: 提交 Custom Hooks

This commit is contained in:
zdl
2025-10-30 13:04:42 +08:00
parent 3472d267af
commit 19a8866305
2 changed files with 266 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
// src/views/Community/components/StockDetailPanel/hooks/useEventStocks.js
import { useSelector, useDispatch } from 'react-redux';
import { useEffect, useCallback, useMemo } from 'react';
import {
fetchEventStocks,
fetchStockQuotes,
fetchEventDetail,
fetchHistoricalEvents,
fetchChainAnalysis,
fetchExpectationScore
} from '../../../../../store/slices/stockSlice';
import { logger } from '../../../../../utils/logger';
/**
* 事件股票数据 Hook
* 封装事件相关的所有数据加载逻辑
*
* @param {string} eventId - 事件ID
* @param {string} eventTime - 事件时间
* @returns {Object} 事件数据和加载状态
*/
export const useEventStocks = (eventId, eventTime) => {
const dispatch = useDispatch();
// 从 Redux 获取数据
const stocks = useSelector(state =>
eventId ? (state.stock.eventStocksCache[eventId] || []) : []
);
const quotes = useSelector(state => state.stock.quotes);
const eventDetail = useSelector(state =>
eventId ? state.stock.eventDetailsCache[eventId] : null
);
const historicalEvents = useSelector(state =>
eventId ? (state.stock.historicalEventsCache[eventId] || []) : []
);
const chainAnalysis = useSelector(state =>
eventId ? state.stock.chainAnalysisCache[eventId] : null
);
const expectationScore = useSelector(state =>
eventId ? state.stock.expectationScores[eventId] : null
);
// 加载状态
const loading = useSelector(state => state.stock.loading);
// 加载所有数据
const loadAllData = useCallback(() => {
if (!eventId) {
logger.warn('useEventStocks', 'eventId 为空,跳过数据加载');
return;
}
logger.debug('useEventStocks', '开始加载事件数据', { eventId });
// 并发加载所有数据
dispatch(fetchEventStocks({ eventId }));
dispatch(fetchEventDetail({ eventId }));
dispatch(fetchHistoricalEvents({ eventId }));
dispatch(fetchChainAnalysis({ eventId }));
dispatch(fetchExpectationScore({ eventId }));
}, [dispatch, eventId]);
// 强制刷新所有数据
const refreshAllData = useCallback(() => {
if (!eventId) return;
logger.debug('useEventStocks', '强制刷新事件数据', { eventId });
dispatch(fetchEventStocks({ eventId, forceRefresh: true }));
dispatch(fetchEventDetail({ eventId, forceRefresh: true }));
dispatch(fetchHistoricalEvents({ eventId, forceRefresh: true }));
dispatch(fetchChainAnalysis({ eventId, forceRefresh: true }));
dispatch(fetchExpectationScore({ eventId }));
}, [dispatch, eventId]);
// 只刷新行情数据
const refreshQuotes = useCallback(() => {
if (stocks.length === 0) return;
const codes = stocks.map(s => s.stock_code);
logger.debug('useEventStocks', '刷新行情数据', {
stockCount: codes.length,
eventTime
});
dispatch(fetchStockQuotes({ codes, eventTime }));
}, [dispatch, stocks, eventTime]);
// 自动加载事件数据
useEffect(() => {
if (eventId) {
loadAllData();
}
}, [loadAllData]);
// 自动加载行情数据
useEffect(() => {
if (stocks.length > 0) {
refreshQuotes();
}
}, [stocks.length, eventId]); // 注意:这里不依赖 refreshQuotes避免重复请求
// 计算股票行情合并数据
const stocksWithQuotes = useMemo(() => {
return stocks.map(stock => ({
...stock,
quote: quotes[stock.stock_code] || null
}));
}, [stocks, quotes]);
return {
// 数据
stocks,
stocksWithQuotes,
quotes,
eventDetail,
historicalEvents,
chainAnalysis,
expectationScore,
// 加载状态
loading: {
stocks: loading.stocks,
quotes: loading.quotes,
eventDetail: loading.eventDetail,
historicalEvents: loading.historicalEvents,
chainAnalysis: loading.chainAnalysis
},
// 方法
loadAllData,
refreshAllData,
refreshQuotes
};
};

View File

@@ -0,0 +1,131 @@
// src/views/Community/components/StockDetailPanel/hooks/useWatchlist.js
import { useSelector, useDispatch } from 'react-redux';
import { useEffect, useCallback, useMemo } from 'react';
import { loadWatchlist, toggleWatchlist as toggleWatchlistAction } from '../../../../../store/slices/stockSlice';
import { message } from 'antd';
import { logger } from '../../../../../utils/logger';
/**
* 自选股管理 Hook
* 封装自选股的加载、添加、移除逻辑
*
* @returns {Object} 自选股数据和操作方法
*/
export const useWatchlist = () => {
const dispatch = useDispatch();
// 从 Redux 获取自选股列表
const watchlistArray = useSelector(state => state.stock.watchlist);
const loading = useSelector(state => state.stock.loading.watchlist);
// 转换为 Set 方便快速查询
const watchlistSet = useMemo(() => {
return new Set(watchlistArray);
}, [watchlistArray]);
// 初始化时加载自选股列表
useEffect(() => {
dispatch(loadWatchlist());
}, [dispatch]);
/**
* 检查股票是否在自选股中
* @param {string} stockCode - 股票代码
* @returns {boolean}
*/
const isInWatchlist = useCallback((stockCode) => {
return watchlistSet.has(stockCode);
}, [watchlistSet]);
/**
* 切换自选股状态
* @param {string} stockCode - 股票代码
* @param {string} stockName - 股票名称
* @returns {Promise<boolean>} 操作是否成功
*/
const toggleWatchlist = useCallback(async (stockCode, stockName) => {
const wasInWatchlist = watchlistSet.has(stockCode);
logger.debug('useWatchlist', '切换自选股状态', {
stockCode,
stockName,
wasInWatchlist
});
try {
await dispatch(toggleWatchlistAction({
stockCode,
stockName,
isInWatchlist: wasInWatchlist
})).unwrap();
message.success(wasInWatchlist ? '已从自选股移除' : '已加入自选股');
return true;
} catch (error) {
logger.error('useWatchlist', '切换自选股失败', error, {
stockCode,
stockName
});
message.error(error.message || '操作失败,请稍后重试');
return false;
}
}, [dispatch, watchlistSet]);
/**
* 批量添加到自选股
* @param {Array<{code: string, name: string}>} stocks - 股票列表
* @returns {Promise<number>} 成功添加的数量
*/
const batchAddToWatchlist = useCallback(async (stocks) => {
logger.debug('useWatchlist', '批量添加自选股', {
count: stocks.length
});
let successCount = 0;
const promises = stocks.map(async ({ code, name }) => {
if (!watchlistSet.has(code)) {
try {
await dispatch(toggleWatchlistAction({
stockCode: code,
stockName: name,
isInWatchlist: false
})).unwrap();
successCount++;
} catch (error) {
logger.error('useWatchlist', '添加失败', error, { code, name });
}
}
});
await Promise.all(promises);
if (successCount > 0) {
message.success(`成功添加 ${successCount} 只股票到自选股`);
}
return successCount;
}, [dispatch, watchlistSet]);
/**
* 刷新自选股列表
*/
const refresh = useCallback(() => {
logger.debug('useWatchlist', '刷新自选股列表');
dispatch(loadWatchlist());
}, [dispatch]);
return {
// 数据
watchlist: watchlistArray,
watchlistSet,
loading,
// 查询方法
isInWatchlist,
// 操作方法
toggleWatchlist,
batchAddToWatchlist,
refresh
};
};