
react 中 `ondrop` 事件不触发,通常是因为缺少 `ondragover` 事件处理器——该事件必须被显式阻止默认行为(`e.preventdefault()`),否则浏览器会拒绝执行 drop 操作。
在原生 HTML 拖放 API 中, 等非表单/非可编辑元素默认不接受拖放目标。即使你绑定了 onDrop,若未同时处理 onDragOver 并调用 e.preventDefault(),浏览器会沿用默认行为(如打开文件、导航跳转等),导致 drop 事件根本不会触发。
你的代码中已正确设置了 onDragStart、onDragEnter、onDragLeave 和 onDrop,但缺失关键的 onDragOver 处理器。只需为每个可投放的容器(即 TODO 和 COMPLETED 列)添加 onDragOver={(e) => e.preventDefault()} 即可激活 onDrop。
以下是修复后的核心修改(仅展示需变更的部分):
TODO
{cards
.filter((c) => c.list === 'TODO')
.map((card) => (
handleDragStart(e, card.id)}
onDragEnter={() => handleDragEnter(card.id)}
onDragLeave={handleDragLeave}
onDragOver={(e) => e.preventDefault()} // ✅ 必须添加!
onDrop={(e) => handleDrop(e, 'TODO')}
>
{card.title}
))}
COMPLETED
{cards
.filter((c) => c.list === 'COMPLETED')
.map((card) => (
handleDragStart(e, card.id)}
onDragEnter={() => handleDragEnter(card.id)}
onDragLeave={handleDragLeave}
onDragOver={(e) => e.preventDefault()} // ✅ 同样必须添加!
onDrop={(e) => handleDrop(e, 'COMPLETED')}
>
{card.title}
))}
⚠️ 注意事项:
- onDragOver 必须返回 e.preventDefault()(不能只写 e.stopPropagation() 或空函数),这是浏览器识别“该区域允许投放”的唯一信号;
- onDragOver 不需要业务逻辑,纯作声明用途,因此可直接内联书写;
- 若需视觉反馈(如高亮目标区),建议结合 onDragEnter / onDragLeave 管理状态(如你已做的 draggedOver),而非依赖 onDragOver;
- ref={dragItem} 在当前逻辑中存在误用:你为每个卡片都绑定了同一 ref,会导致只有最后一个渲染的卡片能被正确引用;如需精确控制拖拽源,应改用 e.currentTarget 或独立 ref 数组(本例中实际未使用 dragItem.current 执行逻辑,可安全移除)。
✅ 总结:
React 的拖放不是“开箱即用”,它严格遵循原生 DOM 规范。牢记三要素:
- 拖拽源:设置 draggable=true + onDragStart(含 setData);
- 投放目标:必须有 onDragOver(含 preventDefault)+ onDrop;
- 可选增强:onDragEnter/onDragLeave 用于 UI 反馈。
无需任何第三方库,纯 React + 原生 API 即可实现健壮的拖放交互。