feat: 创建自定义 Hooks
This commit is contained in:
222
src/components/StockChart/hooks/useKLineData.ts
Normal file
222
src/components/StockChart/hooks/useKLineData.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* useKLineData Hook
|
||||
*
|
||||
* 管理 K 线数据的加载、转换和更新
|
||||
*/
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import type { Chart } from 'klinecharts';
|
||||
import type { ChartType, KLineDataPoint, RawDataPoint } from '../types';
|
||||
import { processChartData } from '../utils/dataAdapter';
|
||||
import { logger } from '@utils/logger';
|
||||
import { stockService } from '@services/stockService';
|
||||
|
||||
export interface UseKLineDataOptions {
|
||||
/** KLineChart 实例 */
|
||||
chart: Chart | null;
|
||||
/** 股票代码 */
|
||||
stockCode: string;
|
||||
/** 图表类型 */
|
||||
chartType: ChartType;
|
||||
/** 事件时间(用于调整数据加载范围) */
|
||||
eventTime?: string;
|
||||
/** 是否自动加载数据 */
|
||||
autoLoad?: boolean;
|
||||
}
|
||||
|
||||
export interface UseKLineDataReturn {
|
||||
/** 处理后的 K 线数据 */
|
||||
data: KLineDataPoint[];
|
||||
/** 原始数据 */
|
||||
rawData: RawDataPoint[];
|
||||
/** 是否加载中 */
|
||||
loading: boolean;
|
||||
/** 加载错误 */
|
||||
error: Error | null;
|
||||
/** 手动加载数据 */
|
||||
loadData: () => Promise<void>;
|
||||
/** 更新数据 */
|
||||
updateData: (newData: KLineDataPoint[]) => void;
|
||||
/** 清空数据 */
|
||||
clearData: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* K 线数据加载和管理 Hook
|
||||
*
|
||||
* @param options 配置选项
|
||||
* @returns UseKLineDataReturn
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error, loadData } = useKLineData({
|
||||
* chart,
|
||||
* stockCode: '600000.SH',
|
||||
* chartType: 'daily',
|
||||
* eventTime: '2024-01-01 10:00:00',
|
||||
* autoLoad: true,
|
||||
* });
|
||||
*/
|
||||
export const useKLineData = (
|
||||
options: UseKLineDataOptions
|
||||
): UseKLineDataReturn => {
|
||||
const {
|
||||
chart,
|
||||
stockCode,
|
||||
chartType,
|
||||
eventTime,
|
||||
autoLoad = true,
|
||||
} = options;
|
||||
|
||||
const [data, setData] = useState<KLineDataPoint[]>([]);
|
||||
const [rawData, setRawData] = useState<RawDataPoint[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
|
||||
/**
|
||||
* 加载数据(从后端 API)
|
||||
*/
|
||||
const loadData = useCallback(async () => {
|
||||
if (!stockCode) {
|
||||
logger.warn('useKLineData', 'loadData', '股票代码为空', { chartType });
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
logger.debug('useKLineData', 'loadData', '开始加载数据', {
|
||||
stockCode,
|
||||
chartType,
|
||||
eventTime,
|
||||
});
|
||||
|
||||
// 调用后端 API 获取数据
|
||||
const response = await stockService.getKlineData(
|
||||
stockCode,
|
||||
chartType,
|
||||
eventTime
|
||||
);
|
||||
|
||||
if (!response || !response.data) {
|
||||
throw new Error('后端返回数据为空');
|
||||
}
|
||||
|
||||
const rawDataList = response.data;
|
||||
setRawData(rawDataList);
|
||||
|
||||
// 数据转换和处理
|
||||
const processedData = processChartData(rawDataList, chartType, eventTime);
|
||||
setData(processedData);
|
||||
|
||||
logger.info('useKLineData', 'loadData', '数据加载成功', {
|
||||
stockCode,
|
||||
chartType,
|
||||
rawCount: rawDataList.length,
|
||||
processedCount: processedData.length,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
logger.error('useKLineData', 'loadData', error, {
|
||||
stockCode,
|
||||
chartType,
|
||||
});
|
||||
setError(error);
|
||||
setData([]);
|
||||
setRawData([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [stockCode, chartType, eventTime]);
|
||||
|
||||
/**
|
||||
* 更新图表数据(使用 DataLoader 模式)
|
||||
*/
|
||||
const updateChartData = useCallback(
|
||||
(klineData: KLineDataPoint[]) => {
|
||||
if (!chart || klineData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// KLineChart 10.0: 使用 setDataLoader 方法
|
||||
chart.setDataLoader({
|
||||
getBars: (params) => {
|
||||
// 将数据传递给图表
|
||||
params.callback(klineData, { more: false });
|
||||
|
||||
logger.debug('useKLineData', 'updateChartData', 'DataLoader 回调', {
|
||||
dataCount: klineData.length,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
logger.debug('useKLineData', 'updateChartData', '图表数据已更新', {
|
||||
dataCount: klineData.length,
|
||||
chartId: chart.id,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error('useKLineData', 'updateChartData', err as Error, {
|
||||
dataCount: klineData.length,
|
||||
});
|
||||
}
|
||||
},
|
||||
[chart]
|
||||
);
|
||||
|
||||
/**
|
||||
* 手动更新数据(外部调用)
|
||||
*/
|
||||
const updateData = useCallback(
|
||||
(newData: KLineDataPoint[]) => {
|
||||
setData(newData);
|
||||
updateChartData(newData);
|
||||
|
||||
logger.debug('useKLineData', 'updateData', '手动更新数据', {
|
||||
newDataCount: newData.length,
|
||||
});
|
||||
},
|
||||
[updateChartData]
|
||||
);
|
||||
|
||||
/**
|
||||
* 清空数据
|
||||
*/
|
||||
const clearData = useCallback(() => {
|
||||
setData([]);
|
||||
setRawData([]);
|
||||
setError(null);
|
||||
|
||||
if (chart) {
|
||||
chart.resetData();
|
||||
logger.debug('useKLineData', 'clearData', '清空数据', {
|
||||
chartId: chart.id,
|
||||
});
|
||||
}
|
||||
}, [chart]);
|
||||
|
||||
// 自动加载数据(当 stockCode/chartType/eventTime 变化时)
|
||||
useEffect(() => {
|
||||
if (autoLoad && stockCode && chart) {
|
||||
loadData();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [stockCode, chartType, eventTime, autoLoad, chart]);
|
||||
|
||||
// 数据变化时更新图表
|
||||
useEffect(() => {
|
||||
if (data.length > 0 && chart) {
|
||||
updateChartData(data);
|
||||
}
|
||||
}, [data, chart, updateChartData]);
|
||||
|
||||
return {
|
||||
data,
|
||||
rawData,
|
||||
loading,
|
||||
error,
|
||||
loadData,
|
||||
updateData,
|
||||
clearData,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user