74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
|
|
"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<string | undefined>(key);
|
||
|
|
const prevPriceRef = useRef<number>(currentPrice);
|
||
|
|
const timerRef = useRef<number | null>(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;
|
||
|
|
}
|