dnd-kit拖拽异常:二次拖拽重叠时元素返回原位置问题求助
问题排查与解决方案
问题概述
- 正常功能:拖拽操作可执行、排序后的列表能正常保存
- 异常现象:首次拖拽完成后,再次拖拽之前被拖动过的元素,当拖拽过程中与其他元素重叠时,该元素会自动返回至初始位置,多个元素存在此问题
- 已尝试方案:调整过渡动画样式、检查组件key值,均无效
- 使用库版本:
@dnd-kit/core: ^6.0.8@dnd-kit/sortable: ^7.0.2@dnd-kit/utilities: ^3.2.1
可能的原因及修复方案
1. 检查自定义Sortable组件的内部逻辑
你使用的自定义Sortable组件大概率是问题核心,需确保它正确集成@dnd-kit/sortable的核心能力:
- 必须用
SortableContext包裹子元素,并传入正确的items数组 - 使用
arrayMove工具处理排序后的数组更新,保证状态不可变 - 在
onDragEnd事件中正确同步列表状态
示例正确的Sortable组件实现:
import { SortableContext, verticalListSortingStrategy, arrayMove } from '@dnd-kit/sortable'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'; export const Sortable = ({ items, sortItems, setSortItems, children }) => { const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor) ); const handleDragEnd = (event) => { const { active, over } = event; if (active.id !== over.id) { const oldIndex = sortItems.findIndex(item => getColumnKey(item) === active.id); const newIndex = sortItems.findIndex(item => getColumnKey(item) === over.id); // 用函数式更新避免闭包导致的旧状态引用问题 setSortItems(prev => arrayMove(prev, oldIndex, newIndex)); } }; return ( <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd} > <SortableContext items={items} strategy={verticalListSortingStrategy} > {children} </SortableContext> </DndContext> ); };
2. 确保元素ID的唯一性与稳定性
- 确认
getColumnKey返回的是唯一且不随位置变化的标识(如数据库ID、UUID),禁止使用索引或位置相关值 - 保证
SortableItem的id、组件key、SortableContext的items数组值完全一致
3. 修复SortableItem的样式与Ref绑定
- 给
SortableItemWrapper添加position: relative,dnd-kit需要基于相对定位计算拖拽偏移 - 移除
SortableItemWrapper上的key属性,key变化会导致组件重新挂载,丢失拖拽状态
修改后的SortableItem:
export const SortableItem = ({ children, id, styles, dragStyles }) => { const { setNodeRef, attributes, listeners, transition, transform, isDragging } = useSortable({ id }); const style = { position: 'relative', transition, transform: transform ? CSS.Translate.toString(transform) : undefined, opacity: isDragging ? 0.7 : 1, zIndex: isDragging ? 1 : 0 }; return ( <SortableItemWrapper ref={setNodeRef} style={{...style, ...styles}}> <DragAct {...listeners} {...attributes} styles={dragStyles} isDragging={isDragging}> <Drag /> </DragAct> <SortableChildren>{children}</SortableChildren> </SortableItemWrapper> ); };
4. 排查过渡动画冲突
暂时注释掉transition属性测试,若问题消失,说明过渡样式存在冲突:
const style = { position: 'relative', // transition, 先注释 transform: transform ? CSS.Translate.toString(transform) : undefined, opacity: isDragging ? 0.7 : 1, zIndex: isDragging ? 1 : 0 };
若确认是过渡问题,需确保自定义样式与dnd-kit提供的transition值无冲突。
内容的提问来源于stack exchange,提问作者Abhishek Chouhan
相关产品推荐
相关产品推荐

