77 lines
2.4 KiB
TypeScript
77 lines
2.4 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { useEffect, useMemo, useState } from "react";
|
||
|
|
import type { DashboardHoldingItem } from "@/features/dashboard/types/dashboard.types";
|
||
|
|
import {
|
||
|
|
type KisRealtimeStockTick,
|
||
|
|
parseKisRealtimeStockTick,
|
||
|
|
} from "@/features/dashboard/utils/kis-stock-realtime.utils";
|
||
|
|
import { useKisWebSocketStore } from "@/features/kis-realtime/stores/kisWebSocketStore";
|
||
|
|
|
||
|
|
const STOCK_REALTIME_TR_ID = "H0STCNT0";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @description 보유 종목 목록에 대한 실시간 체결 데이터를 구독합니다.
|
||
|
|
* @param holdings 보유 종목 목록
|
||
|
|
* @returns 종목별 실시간 체결 데이터/연결 상태
|
||
|
|
* @remarks UI 흐름: DashboardContainer -> useHoldingsRealtime -> HoldingsList/summary 실시간 반영
|
||
|
|
* @see features/dashboard/components/DashboardContainer.tsx 보유종목 실시간 병합
|
||
|
|
*/
|
||
|
|
export function useHoldingsRealtime(holdings: DashboardHoldingItem[]) {
|
||
|
|
const [realtimeData, setRealtimeData] = useState<
|
||
|
|
Record<string, KisRealtimeStockTick>
|
||
|
|
>({});
|
||
|
|
const { subscribe, connect, isConnected } = useKisWebSocketStore();
|
||
|
|
|
||
|
|
const uniqueSymbols = useMemo(
|
||
|
|
() => Array.from(new Set((holdings ?? []).map((item) => item.symbol))).sort(),
|
||
|
|
[holdings],
|
||
|
|
);
|
||
|
|
const symbolKey = useMemo(() => uniqueSymbols.join(","), [uniqueSymbols]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (uniqueSymbols.length === 0) {
|
||
|
|
const resetTimerId = window.setTimeout(() => {
|
||
|
|
setRealtimeData({});
|
||
|
|
}, 0);
|
||
|
|
return () => window.clearTimeout(resetTimerId);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!isConnected) {
|
||
|
|
connect();
|
||
|
|
}
|
||
|
|
|
||
|
|
const unsubs: (() => void)[] = [];
|
||
|
|
|
||
|
|
uniqueSymbols.forEach((symbol) => {
|
||
|
|
const unsub = subscribe(STOCK_REALTIME_TR_ID, symbol, (data) => {
|
||
|
|
const tick = parseKisRealtimeStockTick(data);
|
||
|
|
if (tick) {
|
||
|
|
setRealtimeData((prev) => {
|
||
|
|
const prevTick = prev[tick.symbol];
|
||
|
|
if (
|
||
|
|
prevTick?.currentPrice === tick.currentPrice &&
|
||
|
|
prevTick?.change === tick.change &&
|
||
|
|
prevTick?.changeRate === tick.changeRate
|
||
|
|
) {
|
||
|
|
return prev;
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
...prev,
|
||
|
|
[tick.symbol]: tick,
|
||
|
|
};
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
unsubs.push(unsub);
|
||
|
|
});
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
unsubs.forEach((unsub) => unsub());
|
||
|
|
};
|
||
|
|
}, [symbolKey, uniqueSymbols, connect, subscribe, isConnected]);
|
||
|
|
|
||
|
|
return { realtimeData, isConnected };
|
||
|
|
}
|