.claude/skills/dnd-kit-drag-event-gotchas/SKILL.md
Fix duplicate state updates when using dnd-kit for drag-and-drop. Use when: (1) State changes fire multiple times during a single drag operation, (2) History/audit entries are duplicated for intermediate positions, (3) handleDragOver is updating state that should only change on drop. Covers the difference between onDragOver (continuous) and onDragEnd (final).
npx skillsauth add Dbochman/dotfiles dnd-kit-drag-event-gotchasInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When using dnd-kit's onDragOver handler to track state changes (like movement
history), you get duplicate entries for every column/container the dragged item
passes through, not just the final destination.
handleDragOverhandleDragEnd, not handleDragOverhandleDragStart using a refhandleDragEnd before recording changes// Track starting position
const dragStartRef = useRef<string | null>(null);
const handleDragStart = (event: DragStartEvent) => {
const startContainer = findContainerByItemId(event.active.id);
dragStartRef.current = startContainer?.id || null;
};
const handleDragOver = (event: DragOverEvent) => {
// Only move items between containers here
// Do NOT record history or analytics
};
const handleDragEnd = (event: DragEndEvent) => {
const startId = dragStartRef.current;
const endContainer = findContainerByItemId(event.active.id);
dragStartRef.current = null; // Reset for next drag
// Only record if actually moved to different container
if (startId && endContainer && startId !== endContainer.id) {
// Record history ONCE here
recordMovement(event.active.id, startId, endContainer.id);
}
};
After implementing:
Before fix (problematic):
history: [
{ from: "ideas", to: "todo", timestamp: "...42.716Z" },
{ from: "todo", to: "ideas", timestamp: "...42.720Z" },
{ from: "ideas", to: "todo", timestamp: "...42.727Z" },
{ from: "todo", to: "in-progress", timestamp: "...42.757Z" },
// ... many more oscillating entries
]
After fix (correct):
history: [
{ from: "ideas", to: "in-progress", timestamp: "...42.800Z" }
]
onDragOver fires continuously as the drag passes over drop targetsonDragOver for visual state (hover effects, placeholder positioning)onDragEnd for persistent state changes (database updates, history)onDragOverdevelopment
Search the web for current information, news, facts, and answers. Use when asked questions about current events, needing to look something up, finding websites, researching topics, or when you need up-to-date information beyond your training data.
development
Summarize any URL, YouTube video, podcast, PDF, or file into concise text. Use when asked to read an article, summarize a link, get the gist of a video or podcast, extract content from a URL, or when you need to understand what a web page or document contains.
development
Play music via Spotify and control Google Home speakers. Use when asked to play music, songs, artists, playlists, podcasts, or control speakers/volume/audio.
testing
Create new OpenClaw skills, modify and improve existing skills, and measure skill performance with evals. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy. Also use when asked to "make a skill", "turn this into a skill", "improve this skill", or "test this skill".