mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
fix: preserve Vue node reactivity during undo/redo operations (#7222)
## Summary preserve Vue node reactivity during undo/redo operations Root Cause: The Vue reactivity chain was broken during undo/redo operations: 1. handleDeleteNode was deleting nodeRefs and nodeTriggers 2. Vue components still held references to the old refs 3. When nodes were recreated, finalizeOperation tried to call triggers but they were already deleted 4. Vue didn't know the data had changed, so nodes didn't visually update fix https://github.com/Comfy-Org/ComfyUI_frontend/issues/7040 ## Screenshots https://github.com/user-attachments/assets/2feb294a-36e8-4bbe-b3f7-b7015066abc5 ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7222-fix-preserve-Vue-node-reactivity-during-undo-redo-operations-2c36d73d3650819ab72afb10cbdaf39a) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -956,6 +956,15 @@ class LayoutStoreImpl implements LayoutStore {
|
||||
return this.currentActor
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up refs and triggers for a node when its Vue component unmounts.
|
||||
* This should be called from the component's onUnmounted hook.
|
||||
*/
|
||||
cleanupNodeRef(nodeId: NodeId): void {
|
||||
this.nodeRefs.delete(nodeId)
|
||||
this.nodeTriggers.delete(nodeId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize store with existing nodes
|
||||
*/
|
||||
@@ -964,8 +973,10 @@ class LayoutStoreImpl implements LayoutStore {
|
||||
): void {
|
||||
this.ydoc.transact(() => {
|
||||
this.ynodes.clear()
|
||||
this.nodeRefs.clear()
|
||||
this.nodeTriggers.clear()
|
||||
// Note: We intentionally do NOT clear nodeRefs and nodeTriggers here.
|
||||
// Vue components may already hold references to these refs, and clearing
|
||||
// them would break the reactivity chain. The refs will be reused when
|
||||
// nodes are recreated, and stale refs will be cleaned up over time.
|
||||
this.spatialIndex.clear()
|
||||
this.linkSegmentSpatialIndex.clear()
|
||||
this.slotSpatialIndex.clear()
|
||||
@@ -995,6 +1006,9 @@ class LayoutStoreImpl implements LayoutStore {
|
||||
// Add to spatial index
|
||||
this.spatialIndex.insert(layout.id, layout.bounds)
|
||||
})
|
||||
|
||||
// Trigger all existing refs to notify Vue of the new data
|
||||
this.nodeTriggers.forEach((trigger) => trigger())
|
||||
}, 'initialization')
|
||||
}
|
||||
|
||||
@@ -1085,8 +1099,10 @@ class LayoutStoreImpl implements LayoutStore {
|
||||
if (!this.ynodes.has(operation.nodeId)) return
|
||||
|
||||
this.ynodes.delete(operation.nodeId)
|
||||
this.nodeRefs.delete(operation.nodeId)
|
||||
this.nodeTriggers.delete(operation.nodeId)
|
||||
// Note: We intentionally do NOT delete nodeRefs and nodeTriggers here.
|
||||
// During undo/redo, Vue components may still hold references to the old ref.
|
||||
// If we delete the trigger, Vue won't be notified when the node is re-created.
|
||||
// The trigger will be called in finalizeOperation to notify Vue of the change.
|
||||
|
||||
// Remove from spatial index
|
||||
this.spatialIndex.remove(operation.nodeId)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { computed, toValue } from 'vue'
|
||||
import { computed, onUnmounted, toValue } from 'vue'
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
|
||||
import { useLayoutMutations } from '@/renderer/core/layout/operations/layoutMutations'
|
||||
@@ -17,6 +17,11 @@ export function useNodeLayout(nodeIdMaybe: MaybeRefOrGetter<string>) {
|
||||
// Get the customRef for this node (shared write access)
|
||||
const layoutRef = layoutStore.getNodeLayoutRef(nodeId)
|
||||
|
||||
// Clean up refs and triggers when Vue component unmounts
|
||||
onUnmounted(() => {
|
||||
layoutStore.cleanupNodeRef(nodeId)
|
||||
})
|
||||
|
||||
// Computed properties for easy access
|
||||
const position = computed(() => {
|
||||
const layout = layoutRef.value
|
||||
|
||||
Reference in New Issue
Block a user