import { useState, useEffect, useRef } from 'react'; import { ChartDataService } from '../services/chartDataService'; import type { CandlestickData, MarketData, ChartInterval } from '../types'; interface UseChartDataOptions { symbol: string; interval: ChartInterval; enableRealtime?: boolean; } export function useChartData({ symbol, interval, enableRealtime = true }: UseChartDataOptions) { const [data, setData] = useState([]); const [marketData, setMarketData] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const unsubscribeRef = useRef<(() => void) | null>(null); // Fetch historical data useEffect(() => { let cancelled = false; const fetchData = async () => { try { setIsLoading(true); setError(null); const { start, end } = ChartDataService.getDefaultDateRange(interval); const historicalData = await ChartDataService.getHistoricalData( symbol, interval, start, end ); if (!cancelled) { setData(historicalData); } // Fetch current market data const currentData = await ChartDataService.getRealtimeData(symbol); if (!cancelled) { setMarketData(currentData); } } catch (err) { if (!cancelled) { setError(err instanceof Error ? err.message : 'Failed to load chart data'); } } finally { if (!cancelled) { setIsLoading(false); } } }; fetchData(); return () => { cancelled = true; }; }, [symbol, interval]); // Subscribe to real-time updates useEffect(() => { if (!enableRealtime || isLoading || error) return; const handleRealtimeUpdate = (newCandle: CandlestickData) => { setData(prevData => { const lastCandle = prevData[prevData.length - 1]; // Check if this is an update to the current candle or a new one if (lastCandle && lastCandle.time === newCandle.time) { // Update existing candle return [...prevData.slice(0, -1), newCandle]; } else { // Add new candle return [...prevData, newCandle]; } }); // Update market data setMarketData(prev => { if (!prev) return prev; const change = newCandle.close - (prev.previousClose || prev.price); const changePercent = (change / (prev.previousClose || prev.price)) * 100; return { ...prev, price: newCandle.close, change, changePercent, high: Math.max(prev.high, newCandle.high), low: Math.min(prev.low, newCandle.low), volume: prev.volume + (newCandle.volume || 0), timestamp: new Date().toISOString(), }; }); }; const handleError = (error: Error) => { console.error('Real-time data error:', error); }; unsubscribeRef.current = ChartDataService.subscribeToRealtime( symbol, handleRealtimeUpdate, handleError ); return () => { if (unsubscribeRef.current) { unsubscribeRef.current(); unsubscribeRef.current = null; } }; }, [symbol, enableRealtime, isLoading, error]); const refresh = async () => { const { start, end } = ChartDataService.getDefaultDateRange(interval); try { setIsLoading(true); const newData = await ChartDataService.getHistoricalData(symbol, interval, start, end); setData(newData); const currentData = await ChartDataService.getRealtimeData(symbol); setMarketData(currentData); setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to refresh data'); } finally { setIsLoading(false); } }; return { data, marketData, isLoading, error, refresh, }; }