From 5b1e380660bce54e2bc573b6638288c68f0964cf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Mar 2026 07:09:45 +0000 Subject: [PATCH] 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 --- src/components/WorkflowCanvas.tsx | 5 ++++- src/components/nodes/BaseNode.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index 52ec031c..bd25e2db 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.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} diff --git a/src/components/nodes/BaseNode.tsx b/src/components/nodes/BaseNode.tsx index d5373219..40288b53 100644 --- a/src/components/nodes/BaseNode.tsx +++ b/src/components/nodes/BaseNode.tsx @@ -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); }} > -
{children}
+
{children}
{settingsPanel && (