如何在React JS的dnd-kit/core中实现多选功能?
在dnd-kit中实现多选功能的方案
核心思路
借助dnd-kit官方提供的useSelection钩子管理选中状态,配合DndContext、SortableContext实现多选拖拽,同时通过键盘事件(Ctrl/Command+点击、Shift+点击)支持不同的选中逻辑,拖拽时批量处理选中项的位置更新。
完整实现代码
import { useState } from 'react'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'; import { SortableContext, arrayMove, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable'; import { useSelection } from '@dnd-kit/selection'; // 示例数据源 const initialItems = [ { id: 'item-1', label: '项目1' }, { id: 'item-2', label: '项目2' }, { id: 'item-3', label: '项目3' }, { id: 'item-4', label: '项目4' }, { id: 'item-5', label: '项目5' }, ]; export default function MultiSelectSortableList() { const [items, setItems] = useState(initialItems); // 初始化选中状态管理 const { selectedIds, toggleSelection, toggleRangeSelection, clearSelection } = useSelection({ keys: items.map(item => item.id), }); // 配置拖拽传感器,支持鼠标+键盘操作 const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }) ); // 处理项点击的选中逻辑 const handleItemClick = (id, event) => { if (event.metaKey || event.ctrlKey) { // Ctrl/Command+点击:切换单个项的选中状态 toggleSelection(id); } else if (event.shiftKey) { // Shift+点击:选中连续范围的项 toggleRangeSelection(id); } else { // 普通点击:清空原有选中,只选中当前项 clearSelection(); toggleSelection(id); } }; // 处理拖拽结束后的批量移动逻辑 const handleDragEnd = (event) => { const { active, over } = event; if (!over || active.id === over.id) return; const activeIndex = items.findIndex(item => item.id === active.id); const overIndex = items.findIndex(item => item.id === over.id); // 若拖拽的是选中项之一,批量移动所有选中项 if (selectedIds.has(active.id)) { const selectedItems = items.filter(item => selectedIds.has(item.id)); const remainingItems = items.filter(item => !selectedIds.has(item.id)); // 确定插入位置:根据拖拽方向调整 const insertIndex = activeIndex > overIndex ? overIndex : overIndex + 1; // 重组数组 const newItems = [ ...remainingItems.slice(0, insertIndex), ...selectedItems, ...remainingItems.slice(insertIndex), ]; setItems(newItems); } else { // 单个未选中项拖拽,执行普通排序 setItems(arrayMove(items, activeIndex, overIndex)); } }; // 单个可排序项组件 const SortableItem = ({ id, label }) => { const attributes = useSortable({ id }); const isSelected = selectedIds.has(id); return ( <div {...attributes.attributes} ref={attributes.ref} style={{ ...attributes.style, padding: '12px', margin: '8px 0', border: '1px solid #ddd', backgroundColor: isSelected ? '#e3f2fd' : '#fff', cursor: 'grab', userSelect: 'none', }} onClick={(e) => handleItemClick(id, e)} > <button {...attributes.draggableProps} style={{ border: 'none', background: 'transparent', cursor: 'inherit', width: '100%', textAlign: 'left' }}> {label} </button> </div> ); }; return ( <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd} > <SortableContext items={items.map(item => item.id)} strategy={verticalListSortingStrategy} > <div style={{ maxWidth: '400px', margin: '2rem auto' }}> {items.map(item => ( <SortableItem key={item.id} id={item.id} label={item.label} /> ))} </div> </SortableContext> </DndContext> ); }
关键细节说明
- 选中状态管理:
useSelection钩子提供了开箱即用的选中切换、范围选中、清空选中方法,无需手动维护选中ID集合。 - 多容器适配:如果需要实现多容器间的多选拖拽,只需在多个
SortableContext中共享selectedIds状态,并在handleDragEnd中添加跨容器的项迁移逻辑即可。 - 视觉反馈:通过背景色区分选中项,让用户清晰感知当前选中状态。
- 依赖要求:确保已安装所有必要依赖:
@dnd-kit/core、@dnd-kit/sortable、@dnd-kit/selection
内容的提问来源于stack exchange,提问作者Nooblantis District
相关产品推荐
相关产品推荐

