From 3d7b3e47c9d0f56d5f9a29ccdf092b981dfceb54 Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Sun, 28 Jun 2026 23:28:21 +0200 Subject: [PATCH] feat: instrument analysis --- frontend/src/components/InstrumentChart.tsx | 12 +++++++----- frontend/src/pages/InstrumentDashboard.tsx | 5 ++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/InstrumentChart.tsx b/frontend/src/components/InstrumentChart.tsx index ec0b2ea..66bb251 100644 --- a/frontend/src/components/InstrumentChart.tsx +++ b/frontend/src/components/InstrumentChart.tsx @@ -73,9 +73,11 @@ export default function InstrumentChart({ priceData, indicators, events = [], he const containerRef = useRef(null) const cleanupRef = useRef<(() => void) | null>(null) const onHoverRef = useRef(onDateHover) + const onChartReadyRef = useRef(onChartReady) - // Keep callback ref fresh without triggering chart rebuild + // Keep callback refs fresh without triggering chart rebuild useEffect(() => { onHoverRef.current = onDateHover }, [onDateHover]) + useEffect(() => { onChartReadyRef.current = onChartReady }, [onChartReady]) useEffect(() => { cleanupRef.current?.() @@ -211,9 +213,9 @@ export default function InstrumentChart({ priceData, indicators, events = [], he chart.timeScale().fitContent() // Expose coordinate mapping so siblings can align with the chart x-axis - if (onChartReady && containerRef.current) { + if (onChartReadyRef.current && containerRef.current) { const canvasLeft = containerRef.current.getBoundingClientRect().left - onChartReady((d: string) => chart.timeScale().timeToCoordinate(d as any), canvasLeft) + onChartReadyRef.current((d: string) => chart.timeScale().timeToCoordinate(d as any), canvasLeft) } // ── Star overlay — ★ icons positioned via chart coordinate API ──────── @@ -326,7 +328,7 @@ export default function InstrumentChart({ priceData, indicators, events = [], he chart.timeScale().unsubscribeVisibleLogicalRangeChange(renderStars) if (starsOverlay.parentNode) starsOverlay.parentNode.removeChild(starsOverlay) chart.remove() - onChartReady?.(null as any, 0) + onChartReadyRef.current?.(null as any, 0) } }) @@ -335,7 +337,7 @@ export default function InstrumentChart({ priceData, indicators, events = [], he cleanupRef.current?.() cleanupRef.current = null } - }, [priceData, indicators, events, height, chartType, theoryCurve, onChartReady]) + }, [priceData, indicators, events, height, chartType, theoryCurve]) return (
diff --git a/frontend/src/pages/InstrumentDashboard.tsx b/frontend/src/pages/InstrumentDashboard.tsx index d3452e1..5aa116b 100644 --- a/frontend/src/pages/InstrumentDashboard.tsx +++ b/frontend/src/pages/InstrumentDashboard.tsx @@ -10,6 +10,9 @@ import InstrumentChart, { TheoPoint } from '../components/InstrumentChart' const api = axios.create({ baseURL: '/api' }) +// Stable empty array — prevents InstrumentChart useEffect from re-running on every render +const NO_CHART_EVENTS: never[] = [] + // ── Types ───────────────────────────────────────────────────────────────────── interface CausalTemplate { @@ -1283,7 +1286,7 @@ export default function InstrumentDashboard() {