feat: 创建自定义 Hooks

This commit is contained in:
zdl
2025-11-22 23:14:16 +08:00
parent c391c4c980
commit bcd67ed410
4 changed files with 619 additions and 0 deletions

View 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,
};
};