fix: race condition causing wrong symbol data in chart and evaluation

- Add cancelled flags to evaluation and scorecard effects so stale
  responses from a previous symbol cannot overwrite current data
- Clear evaluation and scorecard state immediately in pickSymbol so
  old data never flashes when switching symbols
- Add key={symbol} to ConfluenceChart to force clean remount on
  symbol change
This commit is contained in:
Investor Flow Build
2026-08-11 23:43:56 -04:00
parent be4c59d3f4
commit e5a7ff9f33
+17 -5
View File
@@ -55,6 +55,9 @@ export function ConfluencePanel() {
setSymbol(sym); setSymbol(sym);
setSearchQuery(sym); setSearchQuery(sym);
setActiveSymbol(sym); setActiveSymbol(sym);
// Clear stale data immediately so old symbol never flashes
setEvaluation(null);
setScorecard(null);
}, [setActiveSymbol]); }, [setActiveSymbol]);
// Sync searchQuery when activeSymbol changes externally (e.g., from another panel) // Sync searchQuery when activeSymbol changes externally (e.g., from another panel)
@@ -62,6 +65,11 @@ export function ConfluencePanel() {
setSearchQuery(activeSymbol); setSearchQuery(activeSymbol);
}, [activeSymbol]); }, [activeSymbol]);
// Sync symbol state when activeSymbol changes externally
useEffect(() => {
setSymbol(activeSymbol);
}, [activeSymbol]);
// Racks // Racks
const [racks, setRacks] = useState<{ system: Array<{ id: string; name: string }>; user: Array<{ id: string; name: string }> }>({ system: [], user: [] }); const [racks, setRacks] = useState<{ system: Array<{ id: string; name: string }>; user: Array<{ id: string; name: string }> }>({ system: [], user: [] });
useEffect(() => { useEffect(() => {
@@ -73,18 +81,22 @@ export function ConfluencePanel() {
const rackId = selectedRackId ?? racks.system[0]?.id ?? null; const rackId = selectedRackId ?? racks.system[0]?.id ?? null;
useEffect(() => { useEffect(() => {
if (!symbol) return; if (!symbol) return;
let cancelled = false;
api.confluence.evaluation(symbol, { rackId: rackId ?? undefined, limit: 6 }) api.confluence.evaluation(symbol, { rackId: rackId ?? undefined, limit: 6 })
.then((r) => setEvaluation(r as Record<string, unknown>)) .then((r) => { if (!cancelled) setEvaluation(r as Record<string, unknown>); })
.catch(() => setEvaluation(null)); .catch(() => { if (!cancelled) setEvaluation(null); });
return () => { cancelled = true; };
}, [symbol, rackId]); }, [symbol, rackId]);
// Scorecard // Scorecard
const [scorecard, setScorecard] = useState<{ slots: Array<Record<string, unknown>>; note: string } | null>(null); const [scorecard, setScorecard] = useState<{ slots: Array<Record<string, unknown>>; note: string } | null>(null);
useEffect(() => { useEffect(() => {
if (!symbol) return; if (!symbol) return;
let cancelled = false;
api.confluence.scorecard(symbol) api.confluence.scorecard(symbol)
.then((r) => setScorecard(r as { slots: Array<Record<string, unknown>>; note: string })) .then((r) => { if (!cancelled) setScorecard(r as { slots: Array<Record<string, unknown>>; note: string }); })
.catch(() => setScorecard(null)); .catch(() => { if (!cancelled) setScorecard(null); });
return () => { cancelled = true; };
}, [symbol]); }, [symbol]);
// Slots // Slots
@@ -194,7 +206,7 @@ export function ConfluencePanel() {
{/* Signal Timeline chart */} {/* Signal Timeline chart */}
{symbol && ( {symbol && (
<CollapsibleSection title="Signal Timeline" defaultOpen> <CollapsibleSection title="Signal Timeline" defaultOpen>
<ConfluenceChart symbol={symbol} /> <ConfluenceChart key={symbol} symbol={symbol} />
</CollapsibleSection> </CollapsibleSection>
)} )}