React拖拽组件的实现
date
Nov 20, 2024
slug
implementation-of-drag-and drop-components
status
Published
tags
react
type
Post
URL
summary
使用 React-Dnd 实现组件的拖拽
基础入门
单个拖拽目标
下面有三个元素,当移入 box 的时候,打印出元素的名字
准备工作-定义基础布局
- 使用DndProvider作为上下文
- 定义基础的页面布局,一个 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
用于记忆化的依赖数组。这就像内置的useMemo
React 钩子一样。默认值是函数 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
用于记忆化的依赖数组。这就像内置的useMemo
React 钩子一样。默认值是函数 spec 的空数组和包含对象 spec 的 spec 的数组。
- 返回的数组
[0] - 收集的 Props
:包含从 collect 函数收集的属性的对象。如果未定义collect
函数,则返回空对象[1] - DragSource Ref
:拖动源的连接器函数。这必须附加到 DOM 的可拖动部分2] - DragPreview Ref
:用于拖动预览的连接器函数。这可能附加到 DOM 的预览部分
如下的代码有几个重要的效果
- 拖动的时候,元素的透明度会发生变化
- 需要在拖动的过程中设置透明度,收集isDragging即可
- 拖动结束的时候,会弹出被拖入的元素的名称
- 需要监听元素拖放结束的时候直接调用alert即可,使用
monitor.getDropResult<DropResult>()
去获取目标元素的结果 - 目标元素的结果是通过在 useDrop 的 hooks 中返回的drop: () => ({ name: 'Dustbin' })这里是一一对应的,drop 表示区域被放置了元素,end 是元素被放置了,两个都是在结束的时候回调的
进阶使用
业务场景
存在 collection1 和 collection2,当然collection 不止两个,有无限个,每个 collection 当中有很多 item,需要变化的就是每个 item 归属的 collection 可以改变,简单来说就是 item可以改变位置,可以拖拽的就是 item
类型定义
定义拖拽区域和拖拽组件
拖拽区域就是每一个 collection,其中DraggableItem就是每一个collection(包括可以拖拽的元素),DropZone是为了防止某个 collection 没有 item 了之后,无法拖入的情况
基于需求,可以分析实际就一个需求
- 在拖拽元素的过程中,需要对数组进行修改
- 使用 hover 去进行监听,自定义一个moveItem方法操作原数组
为什么要使用hover而不是使用 drop,drop是发生在拖动成功的,而 hover 发生在拖动过程中,为了看到拖拽过程中的效果,所以使用 hover
具体的DraggableItem代码实现如下