添加overflow-x-auto后,@hello-pangea/dnd无法向空Card拖拽任务
问题:类Trello应用无法拖拽任务到空分组
我正在开发一款类Trello应用,技术栈为React、TailwindCSS及@hello-pangea/dnd(替代react-beautiful-dnd)。每个分组宽度固定为300px,当分组数量过多超出页面范围时,给Board容器添加overflow-x-auto实现横向滚动,滚动功能正常,但存在问题:无法将任务拖拽到空的分组中,仅能在非空分组内排序。尝试过设置固定高度、添加虚拟空任务等方法,均未完美解决。
完整代码
function Task({ task, index }) { return ( <Draggable draggableId={task._id} index={index} type="task"> {(provided) => ( <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} className="bg-white rounded p-4 shadow mb-4 relative" > {task.name} </div> )} </Draggable> ); } function Section({ section, index }) { return ( <Draggable draggableId={section._id} index={index}> {(provided) => ( <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} className="w-[300px] p-2 mb-4" > <div className="bg-white rounded p-4 shadow mb-4"> <h2 className="text-lg font-bold">{section.name}</h2> <Droppable droppableId={section._id} type="task"> {(provided) => ( <div {...provided.droppableProps} ref={provided.innerRef} className="min-w-full overflow-visible" > {section.tasks.map((task, index) => ( <Task key={task._id} task={task} index={index} sectionId={section._id} /> ))} {provided.placeholder} </div> )} </Droppable> </div> </div> )} </Draggable> ); } function Tasks({ project }) { const [state, setState] = useState(project); const onDragEnd = (result) => { const { destination, source, draggableId, type } = result; // 如果拖拽到可放置区域外,直接返回 if (!destination) { return; } // 如果拖拽到原位置,直接返回 if ( destination.droppableId === source.droppableId && destination.index === source.index ) { return; } if (type === "task") { const sourceSection = state.sections.find( (section) => section._id === source.droppableId ); const destinationSection = state.sections.find( (section) => section._id === destination.droppableId ); // 移动任务到新分组 const task = sourceSection.tasks.splice(source.index, 1)[0]; destinationSection.tasks.splice(destination.index, 0, task); // 更新状态 setState((prevState) => ({ ...prevState, sections: [...prevState.sections], })); } else { // 移动分组到新位置 const section = state.sections.splice(source.index, 1)[0]; state.sections.splice(destination.index, 0, section); // 更新状态 setState((prevState) => ({ ...prevState, sections: [...prevState.sections], })); } }; return ( <div className="flex"> {/* 此处需添加overflow-x-auto */} <DragDropContext onDragEnd={onDragEnd}> <Droppable droppableId="sections" direction="horizontal" className="h-max"> {(provided, snapshot) => ( <div {...provided.droppableProps} ref={provided.innerRef} className="h-full flex"> {state.sections.map((section, index) => ( <Section key={section._id} section={section} index={index} /> ))} {provided.placeholder} </div> )} </Droppable> </DragDropContext> </div> ); }
解决方案
问题核心是空分组内的Droppable容器高度坍缩,导致拖拽检测无法触发。结合横向滚动场景,可通过以下步骤修复:
1. 给空分组的任务容器设置最小高度
修改Section组件中Droppable内部的容器,添加最小高度和内边距,确保空分组有足够的拖拽区域:
<div {...provided.droppableProps} ref={provided.innerRef} className="min-w-full overflow-visible min-h-[60px] p-2" > {section.tasks.map((task, index) => ( <Task key={task._id} task={task} index={index} sectionId={section._id} /> ))} {provided.placeholder} </div>
2. 修复Board容器的滚动样式
给Board外层容器补上overflow-x-auto,同时添加底部内边距避免滚动时分组被截断:
<div className="flex overflow-x-auto pb-4"> <DragDropContext onDragEnd={onDragEnd}> {/* 原有代码 */} </DragDropContext> </div>
3. 优化分组容器的布局结构
给分组的白色卡片容器添加flex flex-col,让任务容器占满剩余空间,进一步强化拖拽区域:
<div className="bg-white rounded p-4 shadow mb-4 flex flex-col"> <h2 className="text-lg font-bold mb-2">{section.name}</h2> <Droppable droppableId={section._id} type="task"> {/* 原有代码 */} </Droppable> </div>
4. 优化状态更新的不可变性(可选)
原代码直接修改数组可能引发隐式状态问题,改用不可变更新方式更安全:
if (type === "task") { setState(prevState => { const newSections = [...prevState.sections]; const sourceIdx = newSections.findIndex(s => s._id === source.droppableId); const destIdx = newSections.findIndex(s => s._id === destination.droppableId); const sourceSection = {...newSections[sourceIdx], tasks: [...newSections[sourceIdx].tasks]}; const destSection = {...newSections[destIdx], tasks: [...newSections[destIdx].tasks]}; const [movedTask] = sourceSection.tasks.splice(source.index, 1); destSection.tasks.splice(destination.index, 0, movedTask); newSections[sourceIdx] = sourceSection; newSections[destIdx] = destSection; return {...prevState, sections: newSections}; }); }
完成以上修改后,空分组会保持可拖拽的最小高度,@hello-pangea/dnd能正确识别放置目标,同时横向滚动体验也会更完善。
内容的提问来源于stack exchange,提问作者blaize
相关产品推荐
相关产品推荐

