# React DnD
# 介绍
React DnD 与大多数拖放库不同,如果您以前从未使用过它,它可能一开始上手较难。但是,一旦您了解了其设计核心的一些概念,它就会开始变得有意义。建议在开始学习之前了解如下的这些概念。其中的一些概念类似于Flux
和 Redux
架构。这不是巧合,因为 React DnD 内部使用 Redux
# 概念
# Items 和 Types
和flux(Redux) 设计类似,React Dnd使用 data
而不是view
,比如在拖动一个东西的时候,我们并不是的描述的说一个DOM结果或者一个组件正在被拖拽,我们说一个确定类型的项目
正在被拖动,将拖动的数据描述为普通对象有助于您保持组件解耦并且彼此不关联。type
很有用,因为随着您的应用程序的增长,您可能希望使更多的东西可拖动,但您不一定希望所有现有的放置目标突然开始对新项目做出反应。这些类型让您可以指定匹配的拖放源
和拖放目标
。您可能会在应用程序中枚举类型常量,类似于枚举 Redux 操作类型的方式。
item: 项目是一个普通的 JavaScript 对象,描述正在拖动的内容 types: 类型是一个字符串(或符号),用于唯一标识应用程序中的一整类项目
# Monitors 监视器
拖放本质上是有状态的。拖动操作正在进行中,或者没有。要么有当前类型和当前项目,要么没有。这种状态必须存在于某个地方。React DnD 通过称为监视器
的内部状态存储上的一些微小包装器将这种状态暴露给您的组件。监视器允许您更新组件的 props 以响应拖放状态更改
。对于每个需要跟踪拖放状态的组件,您可以定义一个collect 收集函数
,从监视器中检索它的相关位。 React DnD 然后负责及时调用您的收集函数并将其返回值合并到组件的道具中。
function collect(monitor) {
return {
highlighted: monitor.canDrop(),
hovered: monitor.isOver()
}
}
collect 函数会返回实时更新的highlighted和hovered值且作为props
传递到组件上
# Connectors 连接器
如果backend处理 DOM 事件,但是组件使用 React 来描述 DOM,那么backend如何知道要监听哪些 DOM 节点? 使用连接器
连接器允许您将预定义角色之一(拖动源、拖动预览或放置目标)分配给渲染函数中的 DOM 节点
function collect(connect, monitor) {
return {
highlighted: monitor.canDrop(),
hovered: monitor.isOver(),
connectDropTarget: connect.dropTarget()
}
}
render() {
const { highlighted, hovered, connectDropTarget } = this.props;
return connectDropTarget(
<div className={classSet({
'Cell': true,
'Cell--highlighted': highlighted,
'Cell--hovered': hovered
})}>
{this.props.children}
</div>
);
}
# Drag Sources and Drop Targets 拖拽源和放置目标
拖动源和放置目标,这是 React DnD 的主要抽象单元。它们将type、item、副作用和collect功能与您的组件联系在一起。
每当你想让一个组件或它的某些部分可拖动时,你需要将该组件包装到一个拖动源声明中。放置目标与拖动源非常相似。唯一的区别是单个放置目标可以同时注册多个项目类型,而不是对应一个项目,它可以处理其hover或drop
# Backends
React DnD使用了html5的drag和drop API,但是在一些移动设备的兼容性并不是特别好,所以React DnD自己维护了一个支持HTML5 drag and drop 插件形式组件,这种可插拔的实现在 React DnD 中称为backend
# Hooks
React-DnD 提供了将组件连接到 DnD 引擎的hook,并允许您收集监视器状态以进行渲染,提供4个hook
- useDrag
- useDrop
- useDragLayer
- useDragDropManager(dev/test hook)
# API
# Components
# DndProvider
DndProvider 组件为您的应用程序提供 React-DnD 功能。这必须通过 backend prop 注入后端,但可以注入 window object
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'
export default class YourApp {
render() {
return (
<DndProvider backend={HTML5Backend}>
/* Your Drag-and-Drop Application */
</DndProvider>
)
}
}
# DragPreviewImage
将 HTML Image 元素呈现为断开连接的拖动预览的组件
import { DragSource, DragPreviewImage } from 'react-dnd'
function DraggableHouse({ connectDragSource, connectDragPreview }) {
return (
<>
<DragPreviewImage src="house_dragged.png" connect={connectDragPreview} />
<div ref={connectDragSource}>🏠</div>
</>
)
}
export default DragSource(
/* ... */
(connect, monitor) => ({
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview()
})
)
# Hooks
# useDrag
useDrag
hook 提供了一种将组件连接到 DnD 系统作为拖动源的方法。
import { useDrag } from 'react-dnd'
function DraggableComponent(props) {
const [collected, drag, dragPreview] = useDrag(() => ({
type,
item: { id }
}))
return collected.isDragging ? (
<div ref={dragPreview} />
) : (
<div ref={drag} {...collected}>
...
</div>
)
}
# useDrop
useDrop hook 提供了一种将组件连接到 DnD 系统作为放置目标
的方法
import { useDrop } from 'react-dnd'
function myDropTarget(props) {
const [collectedProps, drop] = useDrop(() => ({
accept
}))
return <div ref={drop}>Drop Target</div>
}
# useDragLayer
useDragLayer hook 允许您将组件作为拖动层连接到 DnD 系统
import { useDragLayer } from 'react-dnd'
function DragLayerComponent(props) {
const collectedProps = useDragLayer(
monitor => /* Collected Props */
)
return <div>...</div>
}
# useDragDropManager
该挂钩为用户提供了进入 DnD 系统的权限。 DragDropManager 实例是由 React DnD 创建的单例,包含对状态、监视器、后端的访问
import { useDragDropManager } from 'react-dnd'
function Example() {
// The manager provides access to all of React DnD's internals
const dragDropManager = useDragDropManager()
return <div>Example</div>
}
# Monitoring State
- DragSourceMonitor
- DropTargetMonitor
- DragLayerMonitor
# Example
更多的示例可参考:https://react-dnd.github.io/react-dnd/examples