fix(frontend): loadFromState race condition + plugin stale closure (Issues #7, #8)

This commit is contained in:
2026-06-29 23:50:18 +02:00
parent 8493138b4b
commit 275b863f03
3 changed files with 108 additions and 16 deletions
+23 -5
View File
@@ -131,9 +131,9 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
addElement: (el) => setElements((prev) => [...prev, el]),
removeElement: (id) => setElements((prev) => prev.filter((e) => e.id !== id)),
updateElement: (id, props) => setElements((prev) => prev.map((e) => (e.id === id ? { ...e, ...props } : e))),
getElements: () => elements,
getLayers: () => layers,
getActiveLayerId: () => activeLayerId,
getElements: () => elementsRef.current,
getLayers: () => layersRef.current,
getActiveLayerId: () => activeLayerIdRef.current,
showToast: (msg) => setCommandHistory((prev) => [...prev, { prefix: '·', text: msg, type: 'info' }]),
log: (msg) => console.log(`[Plugin] ${msg}`),
};
@@ -177,6 +177,21 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
const groupManagerRef = React.useRef(new GroupManager());
const [groups, setGroups] = useState<ElementGroup[]>([]);
// Issue #8: Refs for PluginContext to avoid stale closures.
// These refs are updated on every render so PluginContext getters always return current state.
const elementsRef = useRef(elements);
elementsRef.current = elements;
const layersRef = useRef(layers);
layersRef.current = layers;
const blocksRef = useRef(blocks);
blocksRef.current = blocks;
const groupsRef = useRef(groups);
groupsRef.current = groups;
const bgConfigRef = useRef(bgConfig);
bgConfigRef.current = bgConfig;
const activeLayerIdRef = useRef(activeLayerId);
activeLayerIdRef.current = activeLayerId;
// Clipboard (for copy/paste)
const clipboardRef = React.useRef<CADElement[] | null>(null);
@@ -265,8 +280,11 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
setSavedStatus('gespeichert');
setDataLoaded(true);
// Push initial data to Yjs CRDT after load
if (collab.status === 'connected') {
const bgConfigs = bgConfig ? [bgConfig] : [];
// Issue #7: Only push to CRDT if backend data actually has content.
// Pushing empty arrays would wipe existing CRDT data from other connected clients.
const bgConfigs = bgConfig ? [bgConfig] : [];
const hasContent = data.elements.length > 0 || data.layers.length > 0 || data.blocks.length > 0 || groups.length > 0 || bgConfigs.length > 0;
if (collab.status === 'connected' && hasContent) {
collab.loadFromState({
elements: data.elements,
layers: data.layers,