Browse Source
Skip hover state updates during node drag to prevent re-renders
When dragging a node over another, onMouseEnter/onMouseLeave were
firing on underlying nodes, updating hoveredNodeId in Zustand and
triggering re-renders of nodes with large base64 images.
- Add isDraggingNodeRef (set on onNodeDragStart, cleared on
onNodeDragStop) to suppress hover events during drag
- Add CSS contain: layout style paint on BaseNode content area to
isolate repaints within each node
https://claude.ai/code/session_01MvD1n4QeXutgwUpKJuDGHa
handoff-20260429-1057
Claude
4 months ago
Failed to extract signature
2 changed files with
8 additions and
5 deletions
src/components/WorkflowCanvas.tsx
src/components/nodes/BaseNode.tsx
@ -268,6 +268,8 @@ const findScrollableAncestor = (target: HTMLElement, deltaX: number, deltaY: num
/** Shared ref so child components (BaseNode) can check panning state without re-rendering */
export const isPanningRef = { current : false } ;
/** Shared ref so child components (BaseNode) can skip hover updates during node drags */
export const isDraggingNodeRef = { current : false } ;
export function WorkflowCanvas() {
const { nodes , edges , groups , isModalOpen , showQuickstart , navigationTarget , canvasNavigationSettings , dimmedNodeIds } =
@ -1964,7 +1966,8 @@ export function WorkflowCanvas() {
onConnectEnd = { handleConnectEnd }
onMoveStart = { ( ) = > { isPanningRef . current = true ; setHoveredNodeId ( null ) ; } }
onMoveEnd = { ( ) = > { isPanningRef . current = false ; } }
onNodeDragStop = { handleNodeDragStop }
onNodeDragStart = { ( ) = > { isDraggingNodeRef . current = true ; } }
onNodeDragStop = { ( event , node ) = > { isDraggingNodeRef . current = false ; handleNodeDragStop ( event , node ) ; } }
onSelectionChange = { handleSelectionChange }
nodeTypes = { nodeTypes }
edgeTypes = { edgeTypes }
@ -3,7 +3,7 @@
import { ReactNode , useCallback , useRef , useLayoutEffect } from "react" ;
import { Node , NodeResizer , OnResize , useReactFlow } from "@xyflow/react" ;
import { useWorkflowStore } from "@/store/workflowStore" ;
import { isPanningRef } from "@/components/WorkflowCanvas" ;
import { isPanningRef , isDraggingNodeRef } from "@/components/WorkflowCanvas" ;
import { getMediaDimensions , calculateAspectFitSize } from "@/utils/nodeDimensions" ;
const DEFAULT_NODE_DIMENSION = 300 ;
@ -297,15 +297,15 @@ export function BaseNode({
$ { className }
` }
onMouseEnter = { ( ) = > {
if ( isPanningRef . current ) return ;
if ( isPanningRef . current || isDraggingNodeRef . current ) return ;
setHoveredNodeId ( id ) ;
} }
onMouseLeave = { ( ) = > {
if ( isPanningRef . current ) return ;
if ( isPanningRef . current || isDraggingNodeRef . current ) return ;
setHoveredNodeId ( null ) ;
} }
>
< div ref = { contentRef } className = { contentClassName ? ? ( fullBleed ? "flex-1 min-h-0 relative" : "px-3 pb-4 flex-1 min-h-0 overflow-hidden flex flex-col" ) } > { children } < / div >
< div ref = { contentRef } style = { { contain : "layout style paint" } } className = { contentClassName ? ? ( fullBleed ? "flex-1 min-h-0 relative" : "px-3 pb-4 flex-1 min-h-0 overflow-hidden flex flex-col" ) } > { children } < / div >
< / div >
{ settingsPanel && (
< div ref = { settingsPanelRef } >