Skip to content

Commit 6d37ceb

Browse files
vanceingallsclaude
andcommitted
fix(studio): use Context.Provider for React 18 and break iframe ref update loop
The #748 decomposition used `<Context value={}>` (React 19 syntax) but the runtime is React 18.3.1, which requires `<Context.Provider value={}>`. This caused all 4 context providers to silently fail to provide values. Also fixes an infinite update loop between handlePreviewIframeRef and NLELayout's onIframeRef effect — the callback identity change triggered the effect which re-called the callback, creating a render loop visible as "Maximum update depth exceeded" in dev mode and intermittent flickering in production. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2f7fcbf commit 6d37ceb

6 files changed

Lines changed: 9 additions & 6 deletions

File tree

packages/studio/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ export function StudioApp() {
261261
const { syncPreviewTimelineHotkey, syncPreviewHistoryHotkey } = appHotkeys;
262262
const handlePreviewIframeRef = useCallback(
263263
(iframe: HTMLIFrameElement | null) => {
264+
if (previewIframeRef.current === iframe) return;
264265
previewIframeRef.current = iframe;
265266
setPreviewIframe(iframe);
266267
syncPreviewTimelineHotkey(iframe);

packages/studio/src/components/nle/NLELayout.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,11 @@ export const NLELayout = memo(function NLELayout({
234234
const currentLevel = compositionStack[compositionStack.length - 1];
235235
const directUrl = compositionStack.length > 1 ? currentLevel.previewUrl : undefined;
236236

237+
const onIframeRefRef = useRef(onIframeRef);
238+
onIframeRefRef.current = onIframeRef;
237239
useEffect(() => {
238-
onIframeRef?.(iframeRef.current);
239-
}, [compositionStack.length, onIframeRef, refreshKey, iframeRef]);
240+
onIframeRefRef.current?.(iframeRef.current);
241+
}, [compositionStack.length, refreshKey, iframeRef]);
240242

241243
// Save master seek position before drilling down so we can restore it on back-navigation.
242244
// saveSeekPosition() sets pendingSeekRef in useTimelinePlayer which onIframeLoad reads.

packages/studio/src/contexts/DomEditContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,5 @@ export function DomEditProvider({
133133
setAgentModalAnchorPoint,
134134
],
135135
);
136-
return <DomEditContext value={stable}>{children}</DomEditContext>;
136+
return <DomEditContext.Provider value={stable}>{children}</DomEditContext.Provider>;
137137
}

packages/studio/src/contexts/FileManagerContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,5 @@ export function FileManagerProvider({
106106
fontAssets,
107107
],
108108
);
109-
return <FileManagerContext value={stable}>{children}</FileManagerContext>;
109+
return <FileManagerContext.Provider value={stable}>{children}</FileManagerContext.Provider>;
110110
}

packages/studio/src/contexts/PanelLayoutContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,5 @@ export function PanelLayoutProvider({
6464
handlePanelResizeEnd,
6565
],
6666
);
67-
return <PanelLayoutContext value={stable}>{children}</PanelLayoutContext>;
67+
return <PanelLayoutContext.Provider value={stable}>{children}</PanelLayoutContext.Provider>;
6868
}

packages/studio/src/contexts/StudioContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,5 @@ export function StudioProvider({
131131
toggleTimelineVisibility,
132132
],
133133
);
134-
return <StudioContext value={stable}>{children}</StudioContext>;
134+
return <StudioContext.Provider value={stable}>{children}</StudioContext.Provider>;
135135
}

0 commit comments

Comments
 (0)