fix(reorder): drag and drop hook#3874
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Normalizes “root” scoping by treating Separately, MCP server refresh now also syncs stored Written by Cursor Bugbot for commit d49a41e. Configure here. |
Greptile SummaryThis PR fixes several inter-related bugs in the sidebar drag-and-drop system that collectively caused incorrect reordering, items silently moving to the wrong folder, and dropped items landing in the wrong position. Key fixes:
Confidence Score: 5/5Safe to merge; the single remaining finding is a minor performance suggestion that does not affect correctness. All findings are P2. The core race-condition fix, null/undefined normalisation, and insert-index correction are all well-reasoned and correctly implemented. The only open point is that createEdgeDropZone still bypasses the deduplication guard (a pre-existing pattern that is now slightly more impactful after throttle removal), which is a style/performance suggestion rather than a data-correctness issue. apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-drag-drop.ts — specifically the createEdgeDropZone handler around line 584. Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant DragHandlers
participant initDragOver
participant setNormalizedDropIndicator
participant dropIndicatorRef
participant ReactState
participant handleDrop
participant handleSelectionDrop
User->>DragHandlers: dragstart
DragHandlers->>dropIndicatorRef: clear cache, isDraggingRef=true
DragHandlers->>ReactState: setIsDragging(true)
loop dragover events (no throttle)
User->>DragHandlers: dragover
DragHandlers->>initDragOver: called
initDragOver-->>DragHandlers: true
DragHandlers->>setNormalizedDropIndicator: indicator candidate
setNormalizedDropIndicator->>setNormalizedDropIndicator: isSameFolderScope() normalise null/undefined
setNormalizedDropIndicator->>setNormalizedDropIndicator: deduplicate (prev === next?)
alt changed
setNormalizedDropIndicator->>dropIndicatorRef: dropIndicatorRef.current = next (sync)
setNormalizedDropIndicator->>ReactState: setDropIndicator(next) (async)
else unchanged
setNormalizedDropIndicator->>dropIndicatorRef: dropIndicatorRef.current = prev (no re-render)
end
end
User->>DragHandlers: drop
DragHandlers->>handleDrop: called
handleDrop->>dropIndicatorRef: read indicator (sync, no stale-state risk)
handleDrop->>dropIndicatorRef: clear (null)
handleDrop->>ReactState: setIsDragging(false), setDropIndicator(null)
handleDrop->>handleSelectionDrop: (indicator, selection)
handleSelectionDrop->>handleSelectionDrop: getSiblingItems() — fresh, bypass cache
handleSelectionDrop->>handleSelectionDrop: getInsertIndexInRemaining() — uses full siblingItems list
handleSelectionDrop->>handleSelectionDrop: buildAndSubmitUpdates()
|
|
bugbot run |
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| return prev | ||
| } | ||
|
|
||
| dropIndicatorRef.current = next |
There was a problem hiding this comment.
Ref update inside deferred updater defeats synchronous guarantee
High Severity
dropIndicatorRef.current is assigned inside the setDropIndicator updater callback, but in React 18 updater functions are deferred (not called synchronously). This means the ref is not updated synchronously during dragOver, so when handleDrop reads dropIndicatorRef.current, it can still hold a stale value — the exact race condition the ref was introduced to prevent (per the comment at lines 45-48). The next value is already computed synchronously before the setDropIndicator call; dropIndicatorRef.current = next needs to be set before entering the updater, similar to how createEdgeDropZone correctly does it.


Summary
Drag and drop fix for reordering in sidebar
Type of Change
Testing
Tested manually
Checklist