• 强大的拖拽组件:React DnD 的使用


    强大的拖拽组件:React DnD 的使用

    17

    文章首发我的个人blog : 原文链接

    学习 React DnD 的最初原因是阅读《如何写一个拖拽日历组件》附的源码时,看不懂拖拽组件 React DnD 的相关代码,于是行动力极强地学习了React DnD这个组件。

    本文会通过 在根组件(Contaier.jsx)展示将垃圾(Box.jsx)扔进垃圾桶(Dustbin.jsx)的例子,解释如何使用React DnD最基本的拖拽用法。

    预览 垃圾桶效果

    查看 垃圾桶源码

    核心API

    想要灵活使用,就先知道几个核心API

    • DragSource 用于包装你需要拖动的组件,使组件能够被拖拽(make it draggable)
    • DropTarget 用于包装接收拖拽元素的组件,使组件能够放置(dropped on it)
    • DragDropContex 用于包装拖拽根组件,DragSource 和 DropTarget 都需要包裹在DragDropContex
    • DragDropContextProvider 与 DragDropContex 类似,用 DragDropContextProvider 元素包裹拖拽根组件。

    大致理解这几个API的概念后,垃圾(Box.jsx)扔进垃圾桶(Dustbin.jsx)的代码将会是:

    // Box.jsx
    import { DragSource } from 'react-dnd';
    
    @DragSource(type, spec, collect)
    export default class Box {
      /* ... */
    }
    
    // Dustbin.jsx
    import { DropTarget } from 'react-dnd';
    
    @DropTarget(types, spec, collect)
    export default class Contaier {
      /* ... */
    }
    
    // Contaier.jsx (DragDropContex)
    import { DragDropContext } from 'react-dnd'
    import HTML5Backend from 'react-dnd-html5-backend'
    import Box from './Box';
    import Dustbin from './Dustbin';
    
    @DragDropContext(HTML5Backend)
    export default class Contaier extends Component {
      render() {
        return (
          <div>
              <Dustbin/>
              <Box/>
          </div>
        );
      }
    }
    
    // 也可以写成 Contaier.jsx (DragDropContextProvider)
    import { DragDropContextProvider } from 'react-dnd'
    import HTML5Backend from 'react-dnd-html5-backend'
    import Box from './Box';
    import Dustbin from './Dustbin';
    
    export default class DustbinContaier extends Component {
      render() {
        return (
          <DragDropContextProvider backend = { HTML5Backend }>
            <div>
                <Dustbin/>
                <Box/>
            </div>
          </DragDropContextProvider>
        );
      }
    }

    API参数介绍

    上面的代码

    @DragSource(type, spec, collect)
    @DropTarget(types, spec, collect)

    可以看到 DragSourceDropTarget 分别有三个参数:

    • type: 拖拽类型,必填
    • spec: 拖拽事件的方法对象,必填。
    • collect: 把拖拽过程中需要信息注入组件的 props,接收两个参数 connect and monitor,必填。
    下面约定 source组件 为DragSource包装的组件(本示例为Box.jsx),target组件 为DropTarget包装的组件(本示例为Dustbin.jsx)。

    type

    当 source组件的type 和 target组件的type 一致时,target组件可以接受source组件

    type的类型可以是 string,symbol,也可以是用一个函数来返回该组件的其他 props。

    翻译为代码:

    // ItemTypes.js 定义类型
    export default {
      BOX: 'box',
    }
    
    // Box.jsx
    import ItemTypes from './ItemTypes'
    @DragSource(ItemTypes.BOX, spec, collect)
    
    // Dustbin.jsx
    import ItemTypes from './ItemTypes'
    @DropTarget(ItemTypes.BOX, spec, collect)

    spec

    spec定义特定方法的对象,如 source组件的spec 可以定义 拖动 相关的事件,target组件的spec 可以定义 放置 相关的事件,具体列表:

    DragSource specObj

    • beginDrag(props, monitor, component): 拖动开始时触发的事件,必须。返回跟props相关的对象。
    • endDrag(props, monitor, component): 拖动结束时触发的事件,可选。
    • canDrag(props, monitor): 当前是否可以拖拽的事件,可选。
    • isDragging(props, monitor): 拖拽时触发的事件,可选。

    翻译为代码:

      // Box.jsx
      const sourceSpec = {
        beginDrag(props, monitor, component){
          // 返回需要注入的属性
          return {
            id: props.id
          }
        },
        endDrag(props, monitor, component){
          // ..
        },
        canDrag(props, monitor){
          // ..
        },
        isDragging(props, monitor){
          // ..
        }
      }
      @DragSource(ItemTypes.BOX, sourceSpec, collect)

    DropTarget specObj

    • drop(props, monitor, component) 组件放下时触发的事件,可选。
    • hover(props, monitor, component) 组件在DropTarget上方时响应的事件,可选。
    • canDrop(props, monitor) 组件可以被放置时触发的事件,可选。

    翻译为代码:

    // Dustbin.jsx
    const targetSpec = {
      drop(props, monitor, component){
        // ..
      },
      hover(props, monitor, component){
        // ..
      },
      canDrop(props, monitor){
        // ..
      }
    }
    @DropTarget(ItemTypes.BOX, targetSpec, collect)

    specObj 对象方法相关参数

    • props: 组件当前的props
    • monitor:查询当前的拖拽状态,比如当前拖拽的item和它的type,当前拖拽的offsets,当前是否dropped。具体获取方法,参看collect 参数 monitor 部分

    • component:当前组件实例

    collect

    collect 是一个函数,默认有两个参数:connect 和 monitor。collect函数将返回一个对象,这个对象会注入到组件的 props 中,也就是说,我们可以通过 this.props 获取collect返回的所有属性。

    参数 connect

    • source组件 collect 中 connect是 DragSourceConnector的实例,它内置了两个方法:dragSource() 和 dragPreview()dragSource()返回一个方法,将source组件传入这个方法,可以将 source DOM 和 React DnD backend 连接起来;dragPreview() 返回一个方法,你可以传入节点,作为拖拽预览时的角色。
    • target组件 collect 中 connect是 DropTargetConnector的实例,内置的方法 dropTarget() 对应 dragSource(),返回可以将 drop target 和 React DnD backend 连接起来的方法。

    翻译为代码:

    // Box.jsx
    @DragSource(ItemTypes.BOX, sourceSpec,(connect)=>({
      connectDragSource: connect.dragSource(),
      connectDragPreview: connect.dragPreview(),
    }))
    export default class Box {
      render() {
        const { connectDragSource } = this.props
        return connectDragSource(
          <div>
           {
               /* ... */
             }
          </div>,
        )
      }
    }
    
    // Dustbin.jsx
    @DropTarget(ItemTypes.BOX, targetSpec, (connect)=>{
      connectDropTarget: connect.dropTarget(),
    })
    export default class Dustbin {
      render() {
        const { connectDropTarget } = this.props
        return connectDropTarget(
          <div>
           {
               /* ... */
             }
          </div>,
        )
      }
    }

    参数 monitor

    monitor 用于查询当前的拖拽状态,其对应实例内置了很多方法。

    内置方法列表:

    // DragSourceMonitor
    monitor.canDrag()        // 是否能被拖拽
    monitor.isDragging()      // 是否正在拖拽
    monitor.getItemType()     // 拖拽组件type
    monitor.getItem()         // 当前拖拽的item
    monitor.getDropResult()   // 查询drop结果
    monitor.didDrop()         // source是否已经drop在target
    monitor.getInitialClientOffset()   // 拖拽组件初始拖拽时offset
    monitor.getInitialSourceClientOffset()
    monitor.getClientOffset() // 拖拽组件当前offset
    monitor.getDifferenceFromInitialOffset() // 当前拖拽offset和初始拖拽offset的差别
    monitor.getSourceClientOffset()
    
    // DropTargetMonitor
    monitor.canDrop()         // 是否可被放置
    monitor.isOver(options)   // source是否在target上方
    monitor.getItemType()     // 拖拽组件type
    monitor.getItem()         // 当前拖拽的item
    monitor.getDropResult()   // 查询drop结果
    monitor.didDrop()         // source是否已经drop在target
    monitor.getInitialClientOffset()   // 拖拽组件初始拖拽时offset
    monitor.getInitialSourceClientOffset()
    monitor.getClientOffset() // 拖拽组件当前offset
    monitor.getDifferenceFromInitialOffset() // 当前拖拽offset和初始拖拽offset的差别
    monitor.getSourceClientOffset()

    具体例子

    先草草丢下官方例子和源码:

    转自:https://segmentfault.com/a/1190000014723549

  • 相关阅读:
    Java入门学习路线目录索引(持续更新中)
    关于技术面试,面试官会怎么考察?
    什么是REST以及 RESTful?
    程序猿一般可以从什么平台接私活
    Statement常用的方法回顾
    信息网络安全协会学习总结提交规范
    20155322 2017-2018-1 《信息安全系统设计》第五周 MyBash实现
    20155322 2017-2018-1《信息安全系统设计》第五周 学习总结
    2017-2018-1 20155322 20155327 实验一 开发环境的熟悉
    20155322 2017-2018-1《信息安全系统设计》第四周学习总结
  • 原文地址:https://www.cnblogs.com/jcxfighting/p/10431049.html
Copyright © 2020-2023  润新知