react-dnd:实现跨表格的行拖拽
react-dnd:实现跨表格的行拖拽
时间:2019.03.20
应用场景
需求:
DanaStudio v4.2中数据治理 - 标准化:实现两个表格间行数据的拖拽联动效果
效果图:
图一
由Table拖拽排序带来的思考
一些尝试
最初曾尝试通过对Table组件的 columns 属性进行包裹,以期达到封装 DragSource 和 DropTarget 的效果,但后来发现,antd根部不会识别这样的修改。
那么antd到底是否开放了一些自定义事件供开发者使用呢?
官方案例
antd官方文档提供了Table单一表格的行拖拽排序,并没有暴露出更多可详细配置的API。
在拖拽排序的案例中,BodyRow既是DragSource,也是DopTarget。那么既然如此,理应也允许针对BodyRow进行二次封装,达到DragSource与DropTarget的独立,从而实现跨表格的行(row)或列(column)的拖拽。
在开始本文的探讨之前,先来回顾一下react dnd的基础知识。
React DnD回顾
- 什么是react dnd
React DnD是一组React 高阶组件 ,它可以帮开发者构建复杂的拖放接口,同时保持组件解耦。借助react dnd的拖动事件在组件之间传输数据,组件根据拖放事件改变它们的外观和状态(数据)。
- 核心API
react-dnd使用的核心API有以下几个:
- DragSource :用于包装你需要拖动的组件,使组件能够被拖拽。
- DropTarget :用于包装接收拖拽元素的组件,使组件能够放置。
- DragDropContext :用于包装拖拽根组件,DragSource 和 DropTarget 都需要包裹在DragDropContext内。
- DragDropContextProvider :与 DragDropContex 类似,用DragDropContextProvider元素包裹拖拽根组件。
- API参数
@DragSource(type, spec, collect)
@DropTarget(types, spec, collect)
DragSource、DropTarget分别有三个参数: type: 拖拽类型,必填。 spec:拖拽事件的方法对象,必填。 * collect:拖拽过程中需要注入组件的props信息,接收两个参数 connect 和 monitor,必填。
其中:
- type
当source组件的type和target组件的type一致时,target组件可以接受source组件。
其值类型可以使string, symbol,也可以是一个函数来返回该组件的其他props。
- spec
spec是 用来定义特定方法 的一个 对象 ,如source组件的spec可以定义拖动相关的事件,target组件的spec可以定义放置相关的事件。具体如下:
DragSource speObj:
- beginDrag (props, monitor, component) 拖动开始时触发的事件,required。返回与props相关的对象。
- endDrag (props, monitor, component) 拖动结束时触发的事件,optional。
- canDrag (props, monitor) 当前是否可以拖拽的事件,optional。
- isDrag (props, monitor) 拖拽时触发的事件,optional。
// DragSource源组件的 spec 属性
const dragSpec = {
beginDrag(props) {
console.log('%c beginDrag...', 'color: #F8B400', props);
endDrag(props, monitor) {
const item = monitor.getItem();
const dropResult = monitor.getDropResult();
console.log(item, dropResult, 'endDrag...');
};
DropTarget specObj:
- drop (props, monitor, component) 组件被放下时触发的事件,optional。
- hover (props, monitor, component) 组件在DropTarget上方时响应的事件,optional。
- canDrop (props, monitor) 组件可以被放置时触发的事件,可选。
// DropTarget目标组件
const targetSpec = {
drop(props, monitor) {
// dropTarget 行的值
const { record } = props.children[0].props;
// dragSource 行的值
const dragItem = monitor.getItem();
};
其中,specObj这个对象方法中的相关参数为:
- props :组件当前的props。
- monitor :查询当前的拖拽状态, 比如当前拖拽的item和他的type,当前拖拽的offsets,当前是否dropped。
- component :当前组件的实例。
- collect
collect是一个函数,默认有两个参数: connect 和 monitor 。该函数将返回一个对象,这个对象会 被注入到props中 ,也就是说,我们 可以在目标组件中通过 this.props 获取到通过collect注入进来的所有属性 。
// TODO 封装 DropTarget 组件
const DropBodyRow = DropTarget(Types.CARD, targetSpec, (connect, monitor) => {
return {
// 由 collect 函数返回的对象中的API,可以在组件中利用 this.props 获取
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
})(BodyRow);
// 在组件中通过使用 this.props 获取API
class BodyRow extends PureComponent {
render() {
const { connectDropTarget, isOver, canDrop } = this.props;