feat: 创建自定义 Hooks
This commit is contained in:
15
src/components/StockChart/hooks/index.ts
Normal file
15
src/components/StockChart/hooks/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* StockChart 自定义 Hooks 统一导出
|
||||||
|
*
|
||||||
|
* 使用方式:
|
||||||
|
* import { useKLineChart, useKLineData, useEventMarker } from '@components/StockChart/hooks';
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { useKLineChart } from './useKLineChart';
|
||||||
|
export type { UseKLineChartOptions, UseKLineChartReturn } from './useKLineChart';
|
||||||
|
|
||||||
|
export { useKLineData } from './useKLineData';
|
||||||
|
export type { UseKLineDataOptions, UseKLineDataReturn } from './useKLineData';
|
||||||
|
|
||||||
|
export { useEventMarker } from './useEventMarker';
|
||||||
|
export type { UseEventMarkerOptions, UseEventMarkerReturn } from './useEventMarker';
|
||||||
209
src/components/StockChart/hooks/useEventMarker.ts
Normal file
209
src/components/StockChart/hooks/useEventMarker.ts
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/**
|
||||||
|
* useEventMarker Hook
|
||||||
|
*
|
||||||
|
* 管理事件标记的创建、更新和删除
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
|
import type { Chart } from 'klinecharts';
|
||||||
|
import type { EventMarker, KLineDataPoint } from '../types';
|
||||||
|
import {
|
||||||
|
createEventMarkerFromTime,
|
||||||
|
createEventMarkerOverlay,
|
||||||
|
removeAllEventMarkers,
|
||||||
|
} from '../utils/eventMarkerUtils';
|
||||||
|
import { logger } from '@utils/logger';
|
||||||
|
|
||||||
|
export interface UseEventMarkerOptions {
|
||||||
|
/** KLineChart 实例 */
|
||||||
|
chart: Chart | null;
|
||||||
|
/** K 线数据(用于定位标记) */
|
||||||
|
data: KLineDataPoint[];
|
||||||
|
/** 事件时间(ISO 字符串) */
|
||||||
|
eventTime?: string;
|
||||||
|
/** 事件标题(用于标记标签) */
|
||||||
|
eventTitle?: string;
|
||||||
|
/** 是否自动创建标记 */
|
||||||
|
autoCreate?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseEventMarkerReturn {
|
||||||
|
/** 当前标记 */
|
||||||
|
marker: EventMarker | null;
|
||||||
|
/** 标记 ID(已添加到图表) */
|
||||||
|
markerId: string | null;
|
||||||
|
/** 创建标记 */
|
||||||
|
createMarker: (time: string, label: string, color?: string) => void;
|
||||||
|
/** 移除标记 */
|
||||||
|
removeMarker: () => void;
|
||||||
|
/** 移除所有标记 */
|
||||||
|
removeAllMarkers: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件标记管理 Hook
|
||||||
|
*
|
||||||
|
* @param options 配置选项
|
||||||
|
* @returns UseEventMarkerReturn
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { marker, createMarker, removeMarker } = useEventMarker({
|
||||||
|
* chart,
|
||||||
|
* data,
|
||||||
|
* eventTime: '2024-01-01 10:00:00',
|
||||||
|
* eventTitle: '重大公告',
|
||||||
|
* autoCreate: true,
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export const useEventMarker = (
|
||||||
|
options: UseEventMarkerOptions
|
||||||
|
): UseEventMarkerReturn => {
|
||||||
|
const {
|
||||||
|
chart,
|
||||||
|
data,
|
||||||
|
eventTime,
|
||||||
|
eventTitle = '事件发生',
|
||||||
|
autoCreate = true,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
const [marker, setMarker] = useState<EventMarker | null>(null);
|
||||||
|
const [markerId, setMarkerId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建事件标记
|
||||||
|
*/
|
||||||
|
const createMarker = useCallback(
|
||||||
|
(time: string, label: string, color?: string) => {
|
||||||
|
if (!chart || !data || data.length === 0) {
|
||||||
|
logger.warn('useEventMarker', 'createMarker', '图表或数据未准备好', {
|
||||||
|
hasChart: !!chart,
|
||||||
|
dataLength: data?.length || 0,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 创建事件标记配置
|
||||||
|
const eventMarker = createEventMarkerFromTime(time, label, color);
|
||||||
|
setMarker(eventMarker);
|
||||||
|
|
||||||
|
// 2. 创建 Overlay
|
||||||
|
const overlay = createEventMarkerOverlay(eventMarker, data);
|
||||||
|
|
||||||
|
if (!overlay) {
|
||||||
|
logger.warn('useEventMarker', 'createMarker', 'Overlay 创建失败', {
|
||||||
|
eventMarker,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 添加到图表
|
||||||
|
const id = chart.createOverlay(overlay);
|
||||||
|
|
||||||
|
if (!id || (Array.isArray(id) && id.length === 0)) {
|
||||||
|
logger.warn('useEventMarker', 'createMarker', '标记添加失败', {
|
||||||
|
overlay,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualId = Array.isArray(id) ? id[0] : id;
|
||||||
|
setMarkerId(actualId as string);
|
||||||
|
|
||||||
|
logger.info('useEventMarker', 'createMarker', '事件标记创建成功', {
|
||||||
|
markerId: actualId,
|
||||||
|
label,
|
||||||
|
time,
|
||||||
|
chartId: chart.id,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('useEventMarker', 'createMarker', err as Error, {
|
||||||
|
time,
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[chart, data]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除事件标记
|
||||||
|
*/
|
||||||
|
const removeMarker = useCallback(() => {
|
||||||
|
if (!chart || !markerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
chart.removeOverlay(markerId);
|
||||||
|
setMarker(null);
|
||||||
|
setMarkerId(null);
|
||||||
|
|
||||||
|
logger.debug('useEventMarker', 'removeMarker', '移除事件标记', {
|
||||||
|
markerId,
|
||||||
|
chartId: chart.id,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('useEventMarker', 'removeMarker', err as Error, {
|
||||||
|
markerId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [chart, markerId]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除所有标记
|
||||||
|
*/
|
||||||
|
const removeAllMarkers = useCallback(() => {
|
||||||
|
if (!chart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
removeAllEventMarkers(chart);
|
||||||
|
setMarker(null);
|
||||||
|
setMarkerId(null);
|
||||||
|
|
||||||
|
logger.debug('useEventMarker', 'removeAllMarkers', '移除所有事件标记', {
|
||||||
|
chartId: chart.id,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('useEventMarker', 'removeAllMarkers', err as Error);
|
||||||
|
}
|
||||||
|
}, [chart]);
|
||||||
|
|
||||||
|
// 自动创建标记(当 eventTime 和数据都准备好时)
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
autoCreate &&
|
||||||
|
eventTime &&
|
||||||
|
chart &&
|
||||||
|
data &&
|
||||||
|
data.length > 0 &&
|
||||||
|
!markerId // 避免重复创建
|
||||||
|
) {
|
||||||
|
createMarker(eventTime, eventTitle);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [eventTime, chart, data, autoCreate]);
|
||||||
|
|
||||||
|
// 清理:组件卸载时移除所有标记
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (chart && markerId) {
|
||||||
|
try {
|
||||||
|
chart.removeOverlay(markerId);
|
||||||
|
} catch (err) {
|
||||||
|
// 忽略清理时的错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [chart, markerId]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
marker,
|
||||||
|
markerId,
|
||||||
|
createMarker,
|
||||||
|
removeMarker,
|
||||||
|
removeAllMarkers,
|
||||||
|
};
|
||||||
|
};
|
||||||
173
src/components/StockChart/hooks/useKLineChart.ts
Normal file
173
src/components/StockChart/hooks/useKLineChart.ts
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
* useKLineChart Hook
|
||||||
|
*
|
||||||
|
* 管理 KLineChart 实例的初始化、配置和销毁
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { init, dispose } from 'klinecharts';
|
||||||
|
import type { Chart } from 'klinecharts';
|
||||||
|
import { useColorMode } from '@chakra-ui/react';
|
||||||
|
import { getTheme } from '../config/klineTheme';
|
||||||
|
import { CHART_INIT_OPTIONS } from '../config';
|
||||||
|
import { logger } from '@utils/logger';
|
||||||
|
|
||||||
|
export interface UseKLineChartOptions {
|
||||||
|
/** 图表容器 ID */
|
||||||
|
containerId: string;
|
||||||
|
/** 图表高度(px) */
|
||||||
|
height?: number;
|
||||||
|
/** 是否自动调整大小 */
|
||||||
|
autoResize?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseKLineChartReturn {
|
||||||
|
/** KLineChart 实例 */
|
||||||
|
chart: Chart | null;
|
||||||
|
/** 容器 Ref */
|
||||||
|
chartRef: React.RefObject<HTMLDivElement>;
|
||||||
|
/** 是否已初始化 */
|
||||||
|
isInitialized: boolean;
|
||||||
|
/** 初始化错误 */
|
||||||
|
error: Error | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KLineChart 初始化和生命周期管理 Hook
|
||||||
|
*
|
||||||
|
* @param options 配置选项
|
||||||
|
* @returns UseKLineChartReturn
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { chart, chartRef, isInitialized } = useKLineChart({
|
||||||
|
* containerId: 'kline-chart',
|
||||||
|
* height: 400,
|
||||||
|
* autoResize: true,
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export const useKLineChart = (
|
||||||
|
options: UseKLineChartOptions
|
||||||
|
): UseKLineChartReturn => {
|
||||||
|
const { containerId, height = 400, autoResize = true } = options;
|
||||||
|
|
||||||
|
const chartRef = useRef<HTMLDivElement>(null);
|
||||||
|
const chartInstanceRef = useRef<Chart | null>(null);
|
||||||
|
const [isInitialized, setIsInitialized] = useState(false);
|
||||||
|
const [error, setError] = useState<Error | null>(null);
|
||||||
|
|
||||||
|
const { colorMode } = useColorMode();
|
||||||
|
|
||||||
|
// 图表初始化
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chartRef.current) {
|
||||||
|
logger.warn('useKLineChart', 'init', '图表容器未挂载', { containerId });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.debug('useKLineChart', 'init', '开始初始化图表', {
|
||||||
|
containerId,
|
||||||
|
height,
|
||||||
|
colorMode,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化图表实例(KLineChart 10.0 API)
|
||||||
|
const chartInstance = init(chartRef.current, {
|
||||||
|
...CHART_INIT_OPTIONS,
|
||||||
|
// 设置初始样式(根据主题)
|
||||||
|
styles: getTheme(colorMode),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!chartInstance) {
|
||||||
|
throw new Error('图表初始化失败:返回 null');
|
||||||
|
}
|
||||||
|
|
||||||
|
chartInstanceRef.current = chartInstance;
|
||||||
|
setIsInitialized(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
logger.info('useKLineChart', 'init', '图表初始化成功', {
|
||||||
|
containerId,
|
||||||
|
chartId: chartInstance.id,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const error = err as Error;
|
||||||
|
logger.error('useKLineChart', 'init', error, { containerId });
|
||||||
|
setError(error);
|
||||||
|
setIsInitialized(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理函数:销毁图表实例
|
||||||
|
return () => {
|
||||||
|
if (chartInstanceRef.current) {
|
||||||
|
logger.debug('useKLineChart', 'dispose', '销毁图表实例', {
|
||||||
|
containerId,
|
||||||
|
chartId: chartInstanceRef.current.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispose(chartInstanceRef.current);
|
||||||
|
chartInstanceRef.current = null;
|
||||||
|
setIsInitialized(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [containerId]); // 只在 containerId 变化时重新初始化
|
||||||
|
|
||||||
|
// 主题切换:更新图表样式
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chartInstanceRef.current || !isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newTheme = getTheme(colorMode);
|
||||||
|
chartInstanceRef.current.setStyles(newTheme);
|
||||||
|
|
||||||
|
logger.debug('useKLineChart', 'updateTheme', '更新图表主题', {
|
||||||
|
colorMode,
|
||||||
|
chartId: chartInstanceRef.current.id,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('useKLineChart', 'updateTheme', err as Error, { colorMode });
|
||||||
|
}
|
||||||
|
}, [colorMode, isInitialized]);
|
||||||
|
|
||||||
|
// 容器尺寸变化:调整图表大小
|
||||||
|
useEffect(() => {
|
||||||
|
if (!chartInstanceRef.current || !isInitialized || !autoResize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleResize = () => {
|
||||||
|
if (chartInstanceRef.current) {
|
||||||
|
chartInstanceRef.current.resize();
|
||||||
|
logger.debug('useKLineChart', 'resize', '调整图表大小');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听窗口大小变化
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
|
||||||
|
// 使用 ResizeObserver 监听容器大小变化(更精确)
|
||||||
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
|
if (chartRef.current && typeof ResizeObserver !== 'undefined') {
|
||||||
|
resizeObserver = new ResizeObserver(handleResize);
|
||||||
|
resizeObserver.observe(chartRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', handleResize);
|
||||||
|
if (resizeObserver && chartRef.current) {
|
||||||
|
resizeObserver.unobserve(chartRef.current);
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [isInitialized, autoResize]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
chart: chartInstanceRef.current,
|
||||||
|
chartRef,
|
||||||
|
isInitialized,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
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