React拖拽组件的实现

date
Nov 20, 2024
slug
implementation-of-drag-and drop-components
status
Published
tags
react
type
Post
URL
summary
使用 React-Dnd 实现组件的拖拽

基础入门

单个拖拽目标

下面有三个元素,当移入 box 的时候,打印出元素的名字
notion image

准备工作-定义基础布局

  1. 使用DndProvider作为上下文
  1. 定义基础的页面布局,一个 dustbin 被拖入的区域组件,box 为可以拖动的组件

定义被拖入区域的组件(放置目标)

这里需要用到useDrop的 api,在官网中这个 api 的介绍如下
useDrop钩子提供了一种将组件作为放置目标连接到 DnD 系统的方法。通过将规范传入 useDrop钩子,你可以指定 drop-target 将接受哪些类型的数据项、要收集的 props 等等。此函数返回一个数组,其中包含要附加到 Drop Target 节点的 ref 和收集的 props。
  • 接收参数
    • spec:规范规范对象或创建规范对象的函数。有关 specification 对象的详细信息
      • accept:必需。string, symbol 或array。此放置目标将仅对指定类型的拖动源生成的项目做出反应。
      • drop(item, monitor) :在目标上放置元素时调用
        • hover(item, monitor) :当项悬停在组件上时调用
          • canDrop(item, monitor):使用它来指定放置目标是否能够接受项目,注意:不能在此方法中调用 monitor.canDrop()
          • collect :收集功能。它应该返回一个 props 的普通对象,以返回注入到组件中。它接收两个参数,monitor和 props
          • options :可选
        • deps用于记忆化的依赖数组。这就像内置的 useMemoReact 钩子一样。默认值是函数 spec 的空数组和包含对象 spec 的 spec 的数组。
      • 返回的数组
        • [0] - 收集的 Props:包含从 collect 函数收集的属性的对象。如果未定义 collect函数,则返回空对象。
        • [1] - DropTarget Ref:放置目标的连接器函数。这必须附加到 DOM 的放置目标部分。
       
      上面最难理解的可能就是 collect,通过下面的一个例子就可以知道
      当我们拖动某个元素的
      • canDrop: 表示是否可以放置
      • isOver: 表示拖拽项是否正在目标上方
      当我们拖动下面的三个元素到盒子里面的时候,需要让盒子这个被拖动区域进行颜色的改变,所以有一个isActive的状态来设置盒子的状态
      那么isActive的状态如何确定,取决于这个元素是否被允许拖入以及这个元素是否在目标的上方(在上方的时候也代表这个元素被放置了),在 collect 中收集后返回的对象就可以在钩子当中去接收

      定义可以拖动的元素

      • 接收参数
        • sepc: 规范规范对象或创建规范对象的函数。有关 specification 对象的详细信息,请参阅下文
          • type:必需。这必须是string或者 array。只有为相同类型注册的放置目标才会对此项目做出反应
          • item:必需(对象或函数)
            • 当这是一个对象时,它是一个描述被拖动数据的纯 JavaScript 对象。这是拖放目标可用的有关拖动源的唯一信息,因此选择他们需要了解的最小数据非常重要。您可能想在此处放置一个复杂的引用,但您应该非常努力地避免这样做,因为它会耦合拖动源和放置目标。使用类似 { id }的东西是个好主意。
            • 当这是一个函数时,它将在拖动操作开始时触发,并返回一个表示拖动操作的对象。如果返回 null,则取消拖动操作。
          • previewOptions:可选。描述拖动预览选项的纯 JavaScript 对象。
          • options:可选。一个普通对象,可选地包含以下任何属性:
            • dropEffect:可选:要用于此拖动的拖放效果的类型。(“move” 或 “copy” 是有效值。
          • end(item, monitor):可选。当拖动停止时,调用 end
            • canDrag(monitor):可选。使用它来指定当前是否允许拖动
            • isDragging(monitor):可选。默认情况下,只有启动拖动操作的拖动源才会被视为正在拖动。
            • collect:可选。收集功能。它应该返回一个 props 的普通对象,以返回注入到你的组件中。
          • deps: deps用于记忆化的依赖数组。这就像内置的 useMemoReact 钩子一样。默认值是函数 spec 的空数组和包含对象 spec 的 spec 的数组。
        • 返回的数组
          • [0] - 收集的 Props:包含从 collect 函数收集的属性的对象。如果未定义 collect函数,则返回空对象
          • [1] - DragSource Ref:拖动源的连接器函数。这必须附加到 DOM 的可拖动部分
          • 2] - DragPreview Ref:用于拖动预览的连接器函数。这可能附加到 DOM 的预览部分
         
        如下的代码有几个重要的效果
        1. 拖动的时候,元素的透明度会发生变化
          1. 需要在拖动的过程中设置透明度,收集isDragging即可
        1. 拖动结束的时候,会弹出被拖入的元素的名称
          1. 需要监听元素拖放结束的时候直接调用alert即可,使用 monitor.getDropResult<DropResult>()去获取目标元素的结果
          2. 目标元素的结果是通过在 useDrop 的 hooks 中返回的drop: () => ({ name: 'Dustbin' })这里是一一对应的,drop 表示区域被放置了元素,end 是元素被放置了,两个都是在结束的时候回调的

        进阶使用

        业务场景

        存在 collection1 和 collection2,当然collection 不止两个,有无限个,每个 collection 当中有很多 item,需要变化的就是每个 item 归属的 collection 可以改变,简单来说就是 item可以改变位置,可以拖拽的就是 item
        notion image

        类型定义

        定义拖拽区域和拖拽组件

        拖拽区域就是每一个 collection,其中DraggableItem就是每一个collection(包括可以拖拽的元素),DropZone是为了防止某个 collection 没有 item 了之后,无法拖入的情况
         
        基于需求,可以分析实际就一个需求
        • 在拖拽元素的过程中,需要对数组进行修改
          • 使用 hover 去进行监听,自定义一个moveItem方法操作原数组
          • 为什么要使用hover而不是使用 drop,drop是发生在拖动成功的,而 hover 发生在拖动过程中,为了看到拖拽过程中的效果,所以使用 hover
        具体的DraggableItem代码实现如下

        © Jayden 2024 - 2025