fix(studio): prevent composition switch loop on sub-composition navigation (#754)

Circular state update between activeCompPath and compositionStack caused
the preview to flicker when navigating to sub-compositions and scrubbing.
The cycle: activeCompPath change → useEffect updates compositionStack →
onCompositionChange fires → setActiveCompPath + refreshPreviewDocumentVersion
→ re-render → effect re-evaluates → repeat. Fixed by guarding both sides:
onCompositionChange skips if path unchanged, updateCompositionStack skips
notification if top-of-stack ID unchanged.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-05-12 23:51:50 +02:00
committed by GitHub
co-authored by Claude Opus 4.6
parent b7ae24de0c
commit 59e1d0787f
2 changed files with 11 additions and 4 deletions
@@ -108,8 +108,12 @@ export function StudioPreviewArea({
onCompositionChange={(compPath) => {
// Sync activeCompPath when user drills down via timeline double-click
// or navigates back via breadcrumb — keeps sidebar + thumbnails in sync.
setActiveCompPath(compPath);
refreshPreviewDocumentVersion();
// Guard against no-op updates to prevent circular refresh cascades
// between activeCompPath → compositionStack → onCompositionChange.
if (compPath !== activeCompPath) {
setActiveCompPath(compPath);
refreshPreviewDocumentVersion();
}
}}
onIframeRef={handlePreviewIframeRef}
previewOverlay={
@@ -203,8 +203,11 @@ export const NLELayout = memo(function NLELayout({
const updateCompositionStack: typeof setCompositionStack = useCallback((action) => {
setCompositionStack((prev) => {
const next = typeof action === "function" ? action(prev) : action;
const id = next[next.length - 1]?.id;
queueMicrotask(() => onCompositionChangeRef.current?.(id === "master" ? null : id));
const prevId = prev[prev.length - 1]?.id;
const nextId = next[next.length - 1]?.id;
if (prevId !== nextId) {
queueMicrotask(() => onCompositionChangeRef.current?.(nextId === "master" ? null : nextId));
}
return next;
});
}, []);