React中DnD-Kit跨容器拖拽任务卡片异常问题求助
问题排查与修复方案
针对你遇到的跨列拖拽无响应、偶尔报错Uncaught TypeError: Cannot read properties of undefined (reading 'id')的问题,以下是具体问题点和修复方案:
1. 拖拽结束时未判断over对象是否存在
报错的直接原因是:当拖拽到列的空白区域时,over对象可能为undefined,直接访问over.data.current.sortable会抛出属性读取错误。
修复代码:
const handleDragEnd = ({active, over}) => { // 先判断over是否存在,不存在直接结束 if (!over) { setActiveId(null); return; } const { containerId: activeContainer } = active.data.current.sortable const { containerId: overContainer } = over.data.current.sortable // ... 后续逻辑 }
2. 跨列拖拽的数组更新逻辑错误
当前用arrayMove处理目标列数组,但arrayMove仅适合同数组内元素移动。跨列拖拽需要将任务从原列移除,再插入到目标列的对应位置,而非修改目标列原有元素顺序。
修复代码:
const handleDragEnd = ({active, over}) => { if (!over) { setActiveId(null); return; } const { containerId: activeContainer } = active.data.current.sortable const { containerId: overContainer } = over.data.current.sortable const activeTaskId = active.id; // 同列拖拽:仅调整当前列内元素顺序 if (activeContainer === overContainer) { const oldIndex = tasks[activeContainer].findIndex(obj => obj.id === activeTaskId); const newIndex = tasks[overContainer].findIndex(obj => obj.id === over.id); if (oldIndex !== newIndex) { setTasks(prev => ({ ...prev, [activeContainer]: arrayMove(prev[activeContainer], oldIndex, newIndex) })); } } // 跨列拖拽:移动任务到目标列,同时删除原列的任务 else { const movedTask = tasks[activeContainer].find(task => task.id === activeTaskId); // 原列删除任务 const updatedActiveColumn = tasks[activeContainer].filter(task => task.id !== activeTaskId); // 目标列插入任务(拖拽到空白区域则插入到末尾) const insertIndex = tasks[overContainer].findIndex(obj => obj.id === over.id); const updatedOverColumn = [...tasks[overContainer]]; insertIndex === -1 ? updatedOverColumn.push(movedTask) : updatedOverColumn.splice(insertIndex, 0, movedTask); setTasks(prev => ({ ...prev, [activeContainer]: updatedActiveColumn, [overContainer]: updatedOverColumn })); } setActiveId(null); }
3. SortableContext的items属性传入错误
SortableContext要求items传入可拖拽元素的id数组,但当前传入的是完整任务对象数组,导致dnd-kit无法正确识别可拖拽项,这是跨列拖拽无响应的核心原因之一。
修复代码(修改TasksColumn.js):
<SortableContext items={tasks.map(task => task.id)} id={id} strategy={verticalListSortingStrategy}> <div ref={setNodeRef} className=''> {tasks.map(task => ( <Draggable key={task.id} id={task.id} task={task} /> ))} </div> </SortableContext>
4. Draggable组件的key值不唯一
当前用task.name作为key,若存在同名任务会导致key重复,引发React渲染异常,干扰拖拽逻辑。
修复代码(修改TasksColumn.js中Draggable的key):
<Draggable key={task.id} id={task.id} task={task} />
5. 重复使用useSortable导致冲突
Draggable组件已调用useSortable,TaskCard中再次调用会导致同一个元素绑定多个拖拽实例,引发逻辑冲突。
修复代码:
- 修改Draggable.js,将激活器逻辑移到此处:
const Draggable = ({ id, task }) => { const { setNodeRef, transform, transition, isDragging, attributes, listeners, setActivatorNodeRef } = useSortable({id}); const style = { transform: CSS.Translate.toString(transform), transition, opacity: isDragging ? 0.5 : 1, }; return ( <div ref={setNodeRef} style={style}> <TaskCard id={id} task={task} attributes={attributes} listeners={listeners} setActivatorNodeRef={setActivatorNodeRef} /> </div> ) }
- 修改TaskCard.js,移除内部的useSortable调用:
const TaskCard = ({ id, task, attributes, listeners, setActivatorNodeRef }) => { return ( <div className="py-4 border-b-2 border-grayLight"> <div className=""> <p className="">{task.deadline}</p> <p className="">{task.priority}</p> </div> <ArrowsPointingOutIcon className='rotate-45 w-5 h-5 outline-none' ref={setActivatorNodeRef} {...listeners} {...attributes} /> <p className="">{task.name}</p> <div className=""> <p className="">{task.author}</p> <p className="">{task.time}</p> </div> </div> ) }
内容的提问来源于stack exchange,提问作者rey2197
相关产品推荐
相关产品推荐

