From 4db39881ee266b050bf538ff8da38757499d635c Mon Sep 17 00:00:00 2001 From: Tanish Manku Date: Fri, 8 May 2026 13:16:28 -0700 Subject: [PATCH] artcraft: drop redundant activeEditorRef in PageScene EngineProvider The mirror ref served two purposes that turned out to be unnecessary: the double-create guard `if (activeEditorRef.current) return;` is unreachable (cleanup nulls the ref before the next effect body runs, under both normal lifecycle and React 18 StrictMode), and the cleanup-side fetch can read the editor through closure capture of the local `newEditor` variable instead. cacheRef and onSerializeRef stay -- those are latest-prop holders so the effect can read fresh values on cleanup without putting the props in deps (which would tear down + rebuild the engine on every prop change). --- .../contexts/EngineContext/EngineProvider.tsx | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/frontend/apps/artcraft/app/src/pages/PageScene/contexts/EngineContext/EngineProvider.tsx b/frontend/apps/artcraft/app/src/pages/PageScene/contexts/EngineContext/EngineProvider.tsx index fe90366300..62b610fe90 100644 --- a/frontend/apps/artcraft/app/src/pages/PageScene/contexts/EngineContext/EngineProvider.tsx +++ b/frontend/apps/artcraft/app/src/pages/PageScene/contexts/EngineContext/EngineProvider.tsx @@ -31,10 +31,10 @@ export const EngineProvider = ({ children, }: Props) => { const [editor, setEditor] = useState(null); - const activeEditorRef = useRef(null); // Hold the latest cache + serialize callback in refs so the engine // lifecycle effect can read them in cleanup without re-running on - // every prop change. + // every prop change. (The editor itself doesn't need a mirror ref — + // closure capture of the local `newEditor` handles cleanup.) const cacheRef = useRef(cacheJsonString); cacheRef.current = cacheJsonString; const onSerializeRef = useRef(onSceneSerialized); @@ -50,11 +50,8 @@ export const EngineProvider = ({ // CameraViewCanvas drive this by setting their nodes (and clearing // them to null on unmount). if (!sceneContainer || !editorCanvas || !camViewCanvas) return; - if (activeEditorRef.current) return; const newEditor = new Editor(); - activeEditorRef.current = newEditor; - newEditor.initialize({ sceneToken: sceneToken || "", sceneContainerEl: sceneContainer, @@ -66,22 +63,18 @@ export const EngineProvider = ({ setActiveEditor(newEditor); return () => { - const live = activeEditorRef.current; - if (!live) return; - // Snapshot scene to host-managed cache so we can restore it on // remount. Skip if the scene never finished loading; the host's // last-known-good cache is preserved on its side. - if (live.isEngineDataLoaded()) { - const sceneGenerationMetadata = getSceneGenerationMetaData(live); - const cacheJson = live.save_manager.getSceneJson({ + if (newEditor.isEngineDataLoaded()) { + const sceneGenerationMetadata = getSceneGenerationMetaData(newEditor); + const cacheJson = newEditor.save_manager.getSceneJson({ sceneGenerationMetadata, }); onSerializeRef.current?.(JSON.stringify(cacheJson)); } - live.unmountEngine(); - activeEditorRef.current = null; + newEditor.unmountEngine(); setEditor(null); setActiveEditor(null); };