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 |
|