# react-dnd

返回开源:前端

开发实例:可拖拽Table|开发实例:可拖拽List(Card)

dnd

React DnD 是一组 React 高阶组件,使用的时候只需要使用对应的 API 将目标组件进行包裹,即可实现拖动或接受拖动元素的功能。将拖动的事件转换成对象中对应状态的形式,不需要开发者自己判断拖动状态,只需要在传入的 spec 对象中各个状态属性中做对应处理即可。

yarn add react-dnd react-dnd-html5-backend
1

# DragSource:使组件能够被拖拽

返回顶部

使用 DragSource 包裹住组件,使其可以进行拖动。

import React, { Component } from 'react';
import { DragSource } from 'react-dnd';

const spec = {
 beginDrag(props, monitor, component) {
  // 这里 return 出去的对象属性自行选择,这里只是用 id 作为演示
  return { id: props.id }
 }

 endDrag(props, monitor, component) {
   ...
 }

 canDrag(props, monitor) {
   ...
 }

isDragging(props, monitor) {
...
}
}

const collect = (connect, monitor) => ({
 // 这里返回一个对象,会将对象的属性都赋到组件的 props 中去。这些属性需要自己定义。
 connectDropTarget: connect.dropTarget(),
 id: monitor.getItem().id
})

@DragSource(type, spec, collect)
class MyComponent extends Component {
  /* ... */
}

export default MyComponent;
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
  • type: 必填。字符串,ES6符号或返回给定组件的函数props。只有为相同类型注册的 drop targets 才会对此拖动源生成的项目做出反应
  • spec:必填。一个普通的JavaScript对象,上面有一些允许的方法。它描述了拖动源如何对拖放事件做出反应。
  • collect:必填。收集功能。它应该返回一个普通的对象注入你的组件。它接收两个参数:connect和monitor。
  • options:可选的。一个普通的对象。

# spec 对象中的方法

  • beginDrag(props, monitor, component):必填。当拖动开始时,beginDrag 被调用。您必须返回描述被拖动数据的纯 JavaScript 对象。您返回的内容会被放置到 monitor.getItem() 获取到的对象中。
  • endDrag(props, monitor, component):可选的。当拖动停止时,endDrag 被调用。对于每个 beginDrag,endDrag 都会对应。
  • canDrag(props, monitor): 可选的。用它来指定当前是否允许拖动。如果您想要始终允许它,只需省略此方法即可。注意:您可能无法调用monitor.canDrag() 此方法。
  • isDragging(props, monitor): 可选的。默认情况下,仅启动拖动操作的拖动源被视为拖动。注意:您可能无法调用 monitor.isDragging() 此方法。

# 方法中的参数 props, monitor, component

  • props:当前组件的 props
  • monitor:一个 DragSourceMonitor 实例。使用它来查询有关当前拖动状态的信息,例如当前拖动的项目及其类型,当前和初始坐标和偏移,以及它是否已被删除。
  • component:指定时,它是组件的实例。使用它来访问底层DOM节点以进行位置或大小测量,或调用 setState 以及其他组件方法。isDragging、 canDrag 方法里获取不到 component 这个参数,因为它们被调用时实例可能不可用

# collect 中的 connect 和 monitor 参数

  • connect: 一个 DragSourceConnector 实例。它有两种方法:dragPreview()和dragSource()。
    • dragSource() => (elementOrNode, options?):常用方法,返回一个函数,传递给组件用来将 source DOM 和 React DnD Backend 连接起来
    • dragPreview():返回一个函数,传递给组件用来将拖动时预览的 DOM 节点 和 React DnD Backend 连接起来
  • monitor:一个 DragSourceMonitor 实例。包含下面各种方法:
方法 含义
canDrag() 是否可以被拖拽。如果没有正在进行拖动操作,则返回 true
isDragging() 是否正在被拖动。如果正在进行拖动操作,则返回 true
getItemType() 返回标识当前拖动项的类型的字符串或ES6符号。 如果没有拖动项目,则返回 null
getItem() 返回表示当前拖动项的普通对象。 每个拖动源都必须通过从其beginDrag()方法返回一个对象来指定它。 如果没有拖动项目,则返回 null
getDropResult() 返回表示最后记录的放置 drop result 对象
didDrop() 如果某个 drop target 处理了 drop 事件,则返回 true,否则返回 false。即使 target 没有返回 drop 结果,didDrop() 也会返回true。 在 endDrag() 中使用它来测试任何放置目标是否已处理掉落。 如果在 endDrag() 之外调用,则返回 false
getInitialClientOffset() 返回当前拖动操作开始时指针的{x,y} client 偏移量。 如果没有拖动项目,则返回 null
getInitialSourceClientOffset() 返回当前拖动操作开始时 drag source 组件的根DOM节点的{x,y}client 偏移量。 如果没有拖动项目,则返回 null
getClientOffset() 拖动操作正在进行时,返回指针的最后记录的{x,y}client 偏移量。 如果没有拖动项目,则返回 null
getDifferenceFromInitialOffset() 返回当前拖动操作开始时鼠标的最后记录 client 偏移量与 client 偏移量之间的{x,y}差异。 如果没有拖动项目,则返回 null
getSourceClientOffset() 返回 drag source 组件的根DOM节点的预计{x,y} client 偏移量,基于其在当前拖动操作开始时的位置以及移动差异。 如果没有拖动项目,则返回 null

# DropTarget:使组件能够放置拖拽组件

返回顶部

import React, { Component } from 'react';
import { DropTarget } from 'react-dnd';

const spec = {
 drop(props, monitor, component) {
  // 这里 return 出去的对象属性自行选择,这里只是用 id 作为演示
  return { id: props.id }
 }

 hover(props, monitor, component) {
   ...
 }

 canDrop(props, monitor) {
   ...
 }
}

const collect = (connect, monitor) => ({
 // 这里返回一个对象,会将对象的属性都赋到组件的 props 中去。这些属性需要自己定义。
 connectDropTarget: connect.dropTarget()
})

@DropTarget(type, spec, collect)
class MyComponent extends Component {
 /* ... */
}
export default MyComponent;

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
  • type: 必填。字符串,ES6符号或返回给定组件的函数props。此放置目标仅对指定类型的 drag sources 项目做出反应
    • 当 source组件的type 和 target组件的type 一致时,target组件可以接受source组件。
  • spec:必填。一个普通的JavaScript对象,上面有一些允许的方法。它描述了放置目标如何对拖放事件做出反应。
  • collect:必填。收集功能。它应该返回一个普通的道具对象注入你的组件。它接收两个参数:connect 和 monitor。
  • options:可选的。一个普通的对象。

# DropTarget中spec 对象中的方法

  • drop(props, monitor, component): 可选的。在目标上放置兼容项目时调用。可以返回 undefined 或普通对象。如果返回一个对象,它将成为放置结果,可以使用 monitor.getDropResult() 获取到。
  • hover(props, monitor, component): 可选的。当项目悬停在组件上时调用。您可以检查 monitor.isOver({ shallow: true }) 以测试悬停是仅发生在当前目标上还是嵌套上。
  • canDrop(props, monitor): 可选的。使用它来指定放置目标是否能够接受该项目。如果想要始终允许它,只需省略此方法即可。

文档没有提供按目的处理进入或离开事件的方法。而是 monitor.isOver() 从收集函数返回调用结果,以便我们可以使用 componentDidUpdateReact 钩子函数来处理组件中的进入和离开事件。

# DropTarget方法中的参数 props, monitor, component

  • props:当前组件的 props
  • monitor:一个 DropTargetMonitor 实例。使用它来查询有关当前拖动状态的信息,例如当前拖动的项目及其类型,当前和初始坐标和偏移,是否超过当前目标,以及是否可以删除它。
  • component:指定时,它是组件的实例。使用它来访问底层DOM节点以进行位置或大小测量,或调用 setState 以及其他组件方法。canDrag 方法里获取不到 component 这个参数,因为它们被调用时实例可能不可用。

# DropTarget collect 中的 connect 和 monitor 参数

  • connect: 一个 DropTargetConnector 实例。它只有一种 dropTarget() 方法。
  • dropTarget() => (elementOrNode):常用方法,返回一个函数,传递给组件用来将 target DOM 和 React DnD Backend 连接起来。通过{ connectDropTarget: connect.dropTarget() }从收集函数返回,可以将任何React元素标记为可放置节点。
  • monitor:一个 DropTargetMonitor 实例。包含下面各种方法:
方法 含义
canDrop() 是否可以被放置。如果正在进行拖动操作,则返回true
isOver(options) drag source 是否悬停在 drop target 区域。可以选择传递{ shallow: true }以严格检查是否只有 drag source 悬停,而不是嵌套目标
getItemType() 返回标识当前拖动项的类型的字符串或ES6符号。如果没有拖动项目则返回 null
getItem() 返回表示当前拖动项的普通对象,每个拖动源都必须通过从其beginDrag()方法返回一个对象来指定它。如果没有拖动项目则返回 null
getDropResult() 返回表示最后记录的放置 drop result 对象
didDrop() 如果某个 drop target 处理了 drop 事件,则返回 true,否则返回 false。即使 target 没有返回 drop 结果,didDrop() 也会返回true。 在 endDrag() 中使用它来测试任何放置目标是否已处理掉落。 如果在 endDrag() 之外调用,则返回 false
getInitialClientOffset() 返回当前拖动操作开始时指针的{x,y} client 偏移量。 如果没有拖动项目,则返回 null
getInitialSourceClientOffset() 返回当前拖动操作开始时 drag source 组件的根DOM节点的{x,y}client 偏移量。 如果没有拖动项目,则返回 null
getClientOffset() 拖动操作正在进行时,返回指针的最后记录的{x,y}client 偏移量。 如果没有拖动项目,则返回 null
getDifferenceFromInitialOffset() 返回当前拖动操作开始时鼠标的最后记录 client 偏移量与 client 偏移量之间的{x,y}差异。 如果没有拖动项目,则返回 null
getSourceClientOffset() 返回 drag source 组件的根DOM节点的预计{x,y} client 偏移量,基于其在当前拖动操作开始时的位置以及移动差异。 如果没有拖动项目,则返回 null

# DragDropContext & DragDropContextProvider

注意

使用 DragSource 和 DropTarget 包裹的组件,必须放在: DragDropContext 包裹的根组件内部,或者 DragDropContextProvider 根标签的内部。

# DragDropContext

使用 DragDropContext 包装应用程序的根组件以启用 React DnD。

import React, { Component } from 'react';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';

@DragDropContext(HTML5Backend)
class YourApp extends Component {
  /* ... */
}

export default YourApp;
1
2
3
4
5
6
7
8
9
10
  • backend:必填。一个 React DnD 后端。除非您正在编写自定义的,否则建议使用 React DnD 附带的 HTML5Backend。
  • context:backend 依赖。用于自定义后端的上下文对象。例如,HTML5Backend可以为iframe场景注入自定义窗口对象。

# DragDropContextProvider

作为 DragDropContext 的替代方法,您可以使用 DragDropContextProvider 元素为应用程序启用React DnD。与 DragDropContext 类似,这可以通过 backendprop 注入后端,但也可以注入一个 window 对象。

import React, { Component } from 'react';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContextProvider } from 'react-dnd';

export default class YourApp extends Component {
 render() {
  return (
   <DragDropContextProvider backend={HTML5Backend}>
   /* ... */
   </DragDropContextProvider>
  )
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • backend:必填。一个 React DnD 后端。除非您正在编写自定义的,否则建议使用 React DnD 附带的 HTML5Backend。
  • context:backend 依赖。用于自定义后端的上下文对象。例如,HTML5Backend可以为iframe场景注入自定义窗口对象。