You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React DndKit异常:点击拖拽项即触发Drop区域添加,isOver误触发

React DndKit点击拖拽项误触发Drop区域问题的解决方案

问题根源

点击拖拽项未拖动就触发onDragOver和onDragEnd的event.over,核心原因有两点:

  • DndKit默认在点击拖拽元素时就启动拖拽流程,即使没有移动鼠标,此时若拖拽元素与Drop区域的碰撞检测条件满足(比如使用closestCenter时中心接近),就会触发isOver和event.over。
  • 未区分“点击”和“拖拽”动作,导致点击就执行Drop逻辑。

解决方案

1. 在onDragEnd中判断是否发生实际拖拽

通过event.delta判断鼠标是否有有效位移,只有当位移超过阈值时才处理Drop逻辑,过滤纯点击操作。

修改主页面的onDragEnd函数:

const onDragEnd = (event: any) => {
    // 设置位移阈值,过滤纯点击(可根据需求调整数值)
    const hasMoved = Math.abs(event.delta.x) > 2 || Math.abs(event.delta.y) > 2;
    if (!hasMoved) return;

    console.log("event end", event);
    if (event.over && event.over.id === 'dropsite-1') {
        // 执行添加到Drop区域的逻辑
        const draggedItem = items.find(item => item.id === event.active.id);
        if (draggedItem) {
            setDropped(prev => [...prev, draggedItem]);
            // 可选:从原列表移除拖拽项
            setItems(prev => prev.filter(item => item.id !== event.active.id));
        }
    }
};

2. 调整碰撞检测的阈值

给closestCenter添加threshold配置,只有当拖拽元素中心进入Drop区域一定比例时才触发碰撞检测,避免点击时误触发。

修改DndContext的collisionDetection参数:

<DndContext
    collisionDetection={closestCenter({ threshold: 0.5 })} // 中心进入目标区域50%才触发碰撞
    id={"context-1"}
    onDragOver={onDragOver}
    onDragEnd={onDragEnd}
>

3. 确保拖拽容器与Drop区域布局无重叠

检查CSS样式,给两个列表容器添加足够间距,避免初始状态下拖拽元素与Drop区域发生碰撞:

.container {
    display: flex;
    gap: 2rem; /* 给左右列表添加间距 */
}

.list-container {
    list-style: none;
    padding: 1rem;
    border: 1px solid #eee;
    min-width: 200px;
}

完整修改后主页面代码

import { useState } from 'react';
import { DndContext, closestCenter } from '@dnd-kit/core';
import { Draggable } from './Draggable';
import { Droppable } from './Droppable';
import { ItemProps, Items } from '@/app/page';

export default function Home() {
    const [items, setItems] = useState<ItemProps[]>(Items);
    const [dropped, setDropped] = useState<ItemProps[]>([]);

    const onDragEnd = (event: any) => {
        const hasMoved = Math.abs(event.delta.x) > 2 || Math.abs(event.delta.y) > 2;
        if (!hasMoved) return;

        console.log("event end", event);
        if (event.over && event.over.id === 'dropsite-1') {
            const draggedItem = items.find(item => item.id === event.active.id);
            if (draggedItem) {
                setDropped(prev => [...prev, draggedItem]);
                setItems(prev => prev.filter(item => item.id !== event.active.id));
            }
        }
    };

    const onDragOver = (event: any) => {
        console.log("event over", event);
    };

    return (
        <DndContext
            collisionDetection={closestCenter({ threshold: 0.5 })}
            id={"context-1"}
            onDragOver={onDragOver}
            onDragEnd={onDragEnd}
        >
            <div className="container">
                <ul className="list-container">
                    {items.map((item) => (
                        <Draggable id={item.id} item={item} key={item.id} />
                    ))}
                </ul>
                <Droppable items={dropped} id="dropsite-1" />
            </div>
        </DndContext>
    );
}

内容的提问来源于stack exchange,提问作者john

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 12:35:02