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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 15:15:26