fix(canvas): defer store disposal until next store is ready — prevent tldraw reactive signals reading disposed store during async React unmount
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
CC Worker 2026-06-01 06:18:24 +00:00
parent 9a7cc86a75
commit 53adc74a1c

View File

@ -65,6 +65,7 @@ export default function SinglePlayerPage() {
error: '' error: ''
}); });
const storeRef = useRef<TLStore | null>(null); const storeRef = useRef<TLStore | null>(null);
const prevStoreRef = useRef<TLStore | null>(null); // holds old store until tldraw has unmounted cleanly
const [storeReady, setStoreReady] = useState(false); const [storeReady, setStoreReady] = useState(false);
// TLDraw user preferences // TLDraw user preferences
@ -139,6 +140,10 @@ export default function SinglePlayerPage() {
}, 2000); }, 2000);
}); });
// Dispose the previous store only AFTER we have a new one ready.
// Disposing while storeReady=true would crash tldraw's reactive signals.
prevStoreRef.current?.dispose();
prevStoreRef.current = null;
storeRef.current = newStore; storeRef.current = newStore;
setStoreReady(true); setStoreReady(true);
setCanvasPhase('ready'); setCanvasPhase('ready');
@ -154,8 +159,13 @@ export default function SinglePlayerPage() {
snapshotServiceRef.current?.forceSaveCurrentNode().catch(() => {}); snapshotServiceRef.current?.forceSaveCurrentNode().catch(() => {});
snapshotServiceRef.current?.clearCurrentNode(); snapshotServiceRef.current?.clearCurrentNode();
snapshotServiceRef.current = null; snapshotServiceRef.current = null;
storeRef.current?.dispose(); // Don't dispose the store here — Tldraw is still subscribed via storeReady=true.
storeRef.current = null; // Move the current store to prevStoreRef; it will be disposed when the next store is ready.
// This prevents tldraw's reactive signals from reading a disposed store during React's async unmount.
if (storeRef.current) {
prevStoreRef.current = storeRef.current;
storeRef.current = null;
}
setStoreReady(false); setStoreReady(false);
setCanvasPhase('idle'); setCanvasPhase('idle');
}; };