React-dnd Intro
React-dnd 简析
对于开源插件或者代码来说,我更喜欢推荐其 github 地址,而不是官网地址:
https://github.com/gaearon/react-dnd
安装
1 2 | |
react-dnd-html5-backend 是一个必要的可选组件,因为 html5 的拖拽API 支持拖动的阴影,总的来说,这插件是必要的。
详细的各名称就不在此说明了,直接介绍关键点吧。
基础
拖拽功能,首先需要知道两个基本的部分:拖起、放下。
拖起,就是鼠标放下并移动的过程;放下,就是鼠标放下拖动元素。 在这一整个过程中,需要声明哪个元素是可以拖动的,哪个元素是可以接收拖动元素的。
react-dnd 中,使用 DragSource 来声明拖动的元素,用DropTarget来声明接收拖动元素的容器。
下面将会通过 看板 的常用功能进行相关的介绍。
看板 基本会涉及到 卡片的上下拖动 和 卡片在不同区间的拖动。
实体 Card, CardBox, Kanban
为了实现基本的功能,需要拖动的实体 Card 有两个基本属性:
1 2 3 | |
Card 实体的简单显示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
CardBox 容器用来接收 Card, 其属性为
1
| |
显示代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
children 表示显示的容器中的多个卡片。
最后,看板的代码就是循环了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
先用 boxes 进行列的划分,再通过其的boxId的判断,来划分 cards对应于哪个列中。
为什么这样子循环来显示卡片,而不是先进行分组,再对组进行渲染。 此处主要考虑到,只想用一个内存来存储所有的卡片。如果分组,即相当于将所有 的卡片分开为多个内存存储,本文不想让其间卡片的内存跳转频繁。
但这实现方法有个缺陷,页面渲染会有空元素,else return '' 会造成一个空的span DOM元素。
所以,注意,此处是用到了卡片的index的。
这是基本实体,它们包含关系为 Kanban > CardBox > Card
DragSource
api
DragSource(type, spec, collect)(MyComponent)
DragSource, 顾名思义,就是可拖动的元素。在本文场景中,拖动元素就是Card了。
API 中,有必要的三个参数,分别是type, spec,collect。
type
type 就是可拖动元素的名称,这会与 DropTarget 的 type 有关联。实际中,名称就是一个字符串,
本文场景中,可以有:
1 2 3 | |
spec
spec 拖动元素的对象,注意,它必须是对象。它定义有四个方法,分别是beginDrag(props, monitor, component),
endDrag(props, monitor, component),canDrag(props, monitor),isDragging(props, monitor),详细说明可链接到
Drag Source Specification
现在,主要对 beginDrag(props, monitor, component) 和 isDragging(props, monitor) 进行说明。
beginDrag(props, monitor, component) 是必须声明的,它要求返回一个普通的对象,例如{ id: props.id }。
其实,它就是将你用的实体,选择拖动使用的属性进行返回。
isDragging(props, monitor) 是可选的,是用来判断一个元素是否是在拖动的过滤中。默认下,一个对象是否是拖动中,
dnd 只会在拖动元素的开始拖动时设置其值。为什么要在这里突出说明一下,因为在操作场景中,初始化的设置在拖动的过程中,
很可能会不正确的,所以需要重新定义其方法实现。
在本文场景中,可以
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
monitor 和 component 还是需要大家了解一下的。
monitor 就是整合拖动实体和拖动状态的新对象,component粗略可以理解为 DOM 元素。
详情看 Drag Source Specification
collect
collect 为一个函数,而函数返回的是一个对象。本质起到桥接作用,将拖动元素的属性和拖动状态整合。
本文场景中,collect 可以为,
1 2 3 4 5 6 | |
综合以上,需要将 Card 声明为可拖动的元素,就是
1
| |
同时,需要调整Card类的代码,将绑定引入的属性声明关联到元素中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
DropTarget
api
DropTarget(types, spec, collect)(MyComponent)
就是接收拖动元素的容器元素。
types
注意,第一个参数是 types,也就是说,它可以接收多种类型,但也可以是字符串。本文场景中,就是ItemTypes.CARD。
spec
同样是普通对象,但不同于拖动元素,容器元素中的说明参数中的方法有drop(props, monitor, component),hover(props, monitor, component),canDrop(props, monitor)。
hover(props, monitor, component) 这里重点说一下此方法,因为这个比较常用,当然drop也很常用,但主要与后台交互时才会用到。比如,不同容器中的拖动,需要将放下时的boxId保存到后台,而hover是实时变化的,不适合与后台交互的action进行数据操作。
在拖动元素从一个容器元素,到另一容器元素时,其的boxid会发生变化,这才合理。
所以,需要在 CardBox 的中定义 changeCard(index, newBoxId) 的方法。
其中, index是所有cards列表中的index。
本文场景中,
1 2 3 4 5 6 7 8 | |
collect
类似于DropSource中的collect, 由于是容器,则没有拖动的属性了。
本文场景中,
1 2 3 4 5 | |
综合上面,将BoxCard声明为拖动容器,就是
1
| |
同时,绑定后,需要在模型中关联渲染的元素,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
另外
基本上,上面大致介绍了一个卡片在不同的容器间相互拖动的故事。
另外,需要指出的是,react-dnd 所有的元素需要使用 DragDropContext 来包裹起来,
1
| |
上面其中有提到,卡片中容器间相互拖动,需要改变其 boxId 的值,
那么,需要在CardBox中传入changeCard(index, newBoxId) 方法属性。
所以,Kanban需要调整为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |