"use client"; import { useEffect, useRef, useState } from "react"; const FLASH_DURATION_MS = 2_000; /** * @description 가격 변동 시 일시 플래시(+/-) 값을 생성합니다. * @param currentPrice 현재가 * @param key 종목 식별 키(종목 변경 시 상태 초기화) * @returns 플래시 값(up/down, 변화량) 또는 null * @remarks UI 흐름: 시세 변경 -> usePriceFlash -> 플래시 값 노출 -> 2초 후 자동 제거 * @see features/dashboard/components/HoldingsList.tsx 보유종목 현재가 플래시 * @see features/dashboard/components/StockDetailPreview.tsx 상세 카드 현재가 플래시 */ export function usePriceFlash(currentPrice: number, key?: string) { const [flash, setFlash] = useState<{ val: number; type: "up" | "down"; id: number; } | null>(null); const prevKeyRef = useRef(key); const prevPriceRef = useRef(currentPrice); const timerRef = useRef(null); useEffect(() => { const keyChanged = prevKeyRef.current !== key; if (keyChanged) { prevKeyRef.current = key; prevPriceRef.current = currentPrice; if (timerRef.current !== null) { window.clearTimeout(timerRef.current); timerRef.current = null; } const resetTimerId = window.setTimeout(() => { setFlash(null); }, 0); return () => window.clearTimeout(resetTimerId); } const prevPrice = prevPriceRef.current; const diff = currentPrice - prevPrice; prevPriceRef.current = currentPrice; if (prevPrice === 0 || Math.abs(diff) === 0) return; // 플래시가 보이는 동안에는 새 플래시를 덮어쓰지 않아 화면 잔상이 지속되지 않게 합니다. if (timerRef.current !== null) return; setFlash({ val: diff, type: diff > 0 ? "up" : "down", id: Date.now(), }); timerRef.current = window.setTimeout(() => { setFlash(null); timerRef.current = null; }, FLASH_DURATION_MS); }, [currentPrice, key]); useEffect(() => { return () => { if (timerRef.current !== null) { window.clearTimeout(timerRef.current); } }; }, []); return flash; }