feat: 提交 Custom Hooks
This commit is contained in:
@@ -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
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -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
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user