Open monsterooo opened 5 years ago
React DnD GitHub - mzabriskie/react-draggable: React draggable component
DnD在内部使用Redux保存数据
后端(Backends): 在DnD中你可以使用可插拔的方法去实现不同平台的拖动功能,在web上一般使用HTML5的drag drop,这种可插拔的方式在DnD中叫做后端。
项目和类型(Items and Types): 在DnD中使用数据而不是视图作为真实的来源,当在屏幕上拖动某些内容的时候,我们不会说正在拖动组件或DOM节点。相反,我们说某个类型(type)的项目(item)正在被拖动。
什么是项目(item)?项目(item)是一个简单的javascript对象,用于描述被拖动的内容。例如在看板应用程序中,当你拖动卡片是,项目可能看起来像{cardId: 42}。将拖动的数据描述为普通的对象有助于保持组件分离并且彼此不知道
{cardId: 42}
DnD通过称为监视器(monitors)的内部状态存储上的几个小包装器将此状态公开给组件。监视器允许你更新组件的poprs以相应拖动状态更改。
假设你想在拖动棋子时突出显示象棋单元格。Cell组件的收集函数(collecting function)可能就像下面这样:
function collect(monitor) { return { highlighted: monitor.canDrop(), hovered: monitor.isOver(), } }
它指示DnD将突出显示的最新值作为props传递给所有Cell实例
实际上,连接器(connectors)作为我们上面描述的收集函数的第一个参数传递。让我们来看看如何使用它来指定放置目标
// collect函数的第一个参数就是连接器 function collect(connect, monitor) { return { highlighted: monitor.canDrop(), hovered: monitor.isOver(), connectDropTarget: connect.dropTarget(), } }
在组件render方法中,我们即可以访问从监视器(monitor)获得的数据和从连接器(connector)获得的函数:
render
render() { const { highlighted, hovered, connectDropTarget } = this.props; return connectDropTarget( <div className={classSet({ 'Cell': true, 'Cell--highlighted': highlighted, 'Cell--hovered': hovered })}> {this.props.children} </div> ); }
connectDropTarget调用告诉DnD我们的根DOM节点是一个有效的放置(drop)目标,并且它的hover和drop事件应该由后端处理。在内部,它通过将一个回调引用附加到你给它的React元素来工作。连接器返回的函数是memoized,因此它不会破坏shouldComponentUpdate优化。
connectDropTarget
memoized
shouldComponentUpdate
拖动源和放置目标(Drag Sources and Drop Targets) 每当你想要使组件或其某部分可拖动时,你需要将该组件包装到拖动源(drag source)申明中。每个拖动都注册了某种类型(type),并且必须实现从组件的props中生成项目(item)的方法。它还可以选择指定一些其他方法来处理拖放时间。拖动源(drag source)声明还允许你指定给定组件的收集函数(collecting function)
高阶组件(Higher-Order)和装饰器(Decorators) 你如何包装组件?包装是什么意思?如果你以前没有使用过高阶组件,请继续阅读本文,因为它详细解释了这个概念。
高阶组件组件只是一个函数,它接收一个React组件,并返回另一个React组件类。库提供的包装组件在其render方法中呈现组将并将props转发给它,但也添加了一些有用的行为。
props
在DnD中,DragSource和DragTarget以及一些其他顶级导出函数实际上是高阶组件。它们向组件中注入拖放魔法。
DragSource
DragTarget
使用它们的一个提示是,它们必须需要两个函数。例如,一下是如何在DragSource中包装YourComponent
YourComponent
import { DragSource } from 'react-dnd' class YourComponent { /* ... */ } export default DragSource(/* ... */)(YourComponent)
请注意,在第一个函数调用中指定DragSource参数之后,还有第二个函数调用,第二个函数调用中传递你的类(组件)。这叫做currying或partial application,并且是装饰器语法开箱即用的必要条件:
currying
partial application
import { DragSource } from 'react-dnd' @DragSource(/* ... */) export default class YourComponent { /* ... */ }
你不需要使用这种装饰器语法,如果你喜欢它,你可以使用Babel来转换你的代码,并将{ "stage": 1 }放入.babelrc文件来启用它。
{ "stage": 1 }
即使你不打算使用装饰器,部分应用程序仍然可以使用,因为你可以使用如_.flow在javascript中组合多个DragSource和DropTarget声明。
_.flow
DropTarget
import { DragSource, DropTarget } from 'react-dnd' import flow from 'lodash/flow' class YourComponent { render() { const { connectDragSource, connectDropTarget } = this.props return connectDragSource( connectDropTarget(), /* ... */ ) } } export default flow( DragSource(/* ... */), DropTarget(/* ... */), )(YourComponent)
import React from 'react' import { DragSource } from 'react-dnd'
// Drag sources和drag targets只有在具有相同的字符串类型是才交互 const Types = { CARD: 'card', }
/**
只需要 beginDrag 功能 */ const cardSource = { beginDrag(props) { // 返回描述拖动项目(item)的数据 const item = { id: props.id } return item },
beginDrag
endDrag(props, monitor, component) { if (!monitor.didDrop()) { return }
// 做一些拖动操作处理 const item = monitor.getItem() const dropResult = monitor.getDropResult() CardActions.moveCardToList(item.id, dropResult.listId) }, }
function Card(props) { // 你的组件可以像以前一样收到props const { id } = props
// 这两个props由DnD注入,由上面的collect函数定义 const { isDragging, connectDragSource } = props
collect
return connectDragSource(
React DnD GitHub - mzabriskie/react-draggable: React draggable component
预览
DnD在内部使用Redux保存数据
后端(Backends): 在DnD中你可以使用可插拔的方法去实现不同平台的拖动功能,在web上一般使用HTML5的drag drop,这种可插拔的方式在DnD中叫做后端。
项目和类型(Items and Types): 在DnD中使用数据而不是视图作为真实的来源,当在屏幕上拖动某些内容的时候,我们不会说正在拖动组件或DOM节点。相反,我们说某个类型(type)的项目(item)正在被拖动。
什么是项目(item)?项目(item)是一个简单的javascript对象,用于描述被拖动的内容。例如在看板应用程序中,当你拖动卡片是,项目可能看起来像
{cardId: 42}
。将拖动的数据描述为普通的对象有助于保持组件分离并且彼此不知道DnD通过称为监视器(monitors)的内部状态存储上的几个小包装器将此状态公开给组件。监视器允许你更新组件的poprs以相应拖动状态更改。
假设你想在拖动棋子时突出显示象棋单元格。Cell组件的收集函数(collecting function)可能就像下面这样:
它指示DnD将突出显示的最新值作为props传递给所有Cell实例
实际上,连接器(connectors)作为我们上面描述的收集函数的第一个参数传递。让我们来看看如何使用它来指定放置目标
在组件
render
方法中,我们即可以访问从监视器(monitor)获得的数据和从连接器(connector)获得的函数:connectDropTarget
调用告诉DnD我们的根DOM节点是一个有效的放置(drop)目标,并且它的hover和drop事件应该由后端处理。在内部,它通过将一个回调引用附加到你给它的React元素来工作。连接器返回的函数是memoized
,因此它不会破坏shouldComponentUpdate
优化。拖动源和放置目标(Drag Sources and Drop Targets) 每当你想要使组件或其某部分可拖动时,你需要将该组件包装到拖动源(drag source)申明中。每个拖动都注册了某种类型(type),并且必须实现从组件的props中生成项目(item)的方法。它还可以选择指定一些其他方法来处理拖放时间。拖动源(drag source)声明还允许你指定给定组件的收集函数(collecting function)
高阶组件(Higher-Order)和装饰器(Decorators) 你如何包装组件?包装是什么意思?如果你以前没有使用过高阶组件,请继续阅读本文,因为它详细解释了这个概念。
高阶组件组件只是一个函数,它接收一个React组件,并返回另一个React组件类。库提供的包装组件在其
render
方法中呈现组将并将props
转发给它,但也添加了一些有用的行为。在DnD中,
DragSource
和DragTarget
以及一些其他顶级导出函数实际上是高阶组件。它们向组件中注入拖放魔法。使用它们的一个提示是,它们必须需要两个函数。例如,一下是如何在
DragSource
中包装YourComponent
请注意,在第一个函数调用中指定
DragSource
参数之后,还有第二个函数调用,第二个函数调用中传递你的类(组件)。这叫做currying
或partial application
,并且是装饰器语法开箱即用的必要条件:你不需要使用这种装饰器语法,如果你喜欢它,你可以使用Babel来转换你的代码,并将
{ "stage": 1 }
放入.babelrc文件来启用它。即使你不打算使用装饰器,部分应用程序仍然可以使用,因为你可以使用如
_.flow
在javascript中组合多个DragSource
和DropTarget
声明。// Drag sources和drag targets只有在具有相同的字符串类型是才交互 const Types = { CARD: 'card', }
/**
只需要
beginDrag
功能 */ const cardSource = { beginDrag(props) { // 返回描述拖动项目(item)的数据 const item = { id: props.id } return item },endDrag(props, monitor, component) { if (!monitor.didDrop()) { return }
// 做一些拖动操作处理 const item = monitor.getItem() const dropResult = monitor.getDropResult() CardActions.moveCardToList(item.id, dropResult.listId) }, }
/**
function Card(props) { // 你的组件可以像以前一样收到props const { id } = props
// 这两个props由DnD注入,由上面的
collect
函数定义 const { isDragging, connectDragSource } = propsreturn connectDragSource(