React Dnd 基本拖放功能实现及 API 整理

创建项目

$ mkdir myapp && cd myapp
$ yarn create @umijs/umi-app
$ yarn add react-dnd react-dnd-html5-backend

简单了解 API

DndProvider

一个容器,在这个容器中的元素可以进行拖放的操作。

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>
    )
  }
}

useDrag

一个 hook 函数,可以让一个 DOM 元素实现拖拽效果。

import { useDrag } from 'react-dnd'

function DraggableComponent(props) {
  const [collectedProps, drag] = useDrag({
    item: { id, type }
  })
  return <div ref={drag}>...</div>
}

useDrop

一个 hook 函数,可以让一个 DOM 元素能够放置拖拽元素。

import { useDrop } from 'react-dnd'

function myDropTarget(props) {
  const [collectedProps, drop] = useDrop({
    accept
  })

  return <div ref={drop}>Drop Target</div>
}

一个简单的栗子

import React from 'react';
import styles from './index.less';
import { useDrop, useDrag, DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'

const ItemTypes = {
  APP: 'demo-app'
}

const Child = () => {
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes.APP },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  })

  return <div className={styles.child} style={{ opacity: isDragging ? 0.5 : 1 }} ref={drag}>
    Child
  </div>
}

const Container = () => {
  const [{ isOver }, drag] = useDrop({
    accept: ItemTypes.APP,
    collect: (monitor) => ({
      isOver: monitor.isOver(),
    }),
  })

  return <div className={styles.container} ref={drag} style={{ background: isOver ? '#FFAA00' : '#FFFFFF' }}>
    Container
  </div>
}

export default () => {
  return (
    <DndProvider backend={HTML5Backend}>
      <div className={styles.app}>
        <Container />
        <Child />
      </div>
    </DndProvider>
  );
}

.app {
  background: #FFFFFF;
}

.container {
  border: #CCCCCC solid 1px;
  width: 300px;
  height: 300px;
}

.child {
  border: #CCCCCC solid 1px;
  display: inline-block;
  margin: 5px;
  width: 50px;
  height: 50px;
}

用到的三个 API 梳理

简单梳理下所有的 API

useDrag

这个 hook 函数返回一个数组。
index 0 一个对象,它是从配置中的 collect 函数来定义的。
index 1 一个连接器函数,用在 React 的 ref 属性。连接了能够拖拽的元素。
index 2 一个连接器函数,用在 React 的 ref 属性。连接了会被拖拽的元素。
PS; 1 和 2 可以用来实现拖拽元素 A 中的元素 B,元素 A 跟着移动。

下面是 Hook 函数中的配置对象元素:

  • item 必填属性,它是一个纯 JavaScript 对象。这个对象中必须传的是 type 属性。另外的一个作用是传递拓转元素中的数据。可以在拓转完成函数 end 中拿到数据做后续处理。
  • item.type 必填属性,只有 drag 和 drop 中的 type 相才能实现拖放。
  • previewOptions 预览配置对象?
  • options 配置对象
  • begin(monitor) 拖拽开始事件,一般不需要返回,如果有 return 会替换 item
  • end(item, monitor) 拖拽结束事件
  • canDrag(monitor) 定义能否拖拽的函数。
  • isDragging(monitor) 用于重写定义正在拖拽中的状态。,一般不用改。
  • collect 收集函数,必须返回一个对象。具体配置可参照 DragSourceMonitor,它返回的对象可以在 index 0 对象中拿到。

个人感觉 item 和 collect 最常用,begin 函数和 end 函数也比较常用。梳理下来还是挺简单的。

useDrop

这个 hook 函数返回一个数组:

index 0 collect 函数返回的对象,如果没有对象可以返回空。
index 1 一个连接器函数,用在 React 的 ref 属性。连接了能够放置的元素。

下面是配置对象元素:

  • accept必填项,定义了一个可以接收的拖拽元素的类型。(就是 useDrag 中的 item.type)
  • options 配置对象
  • drop(item, monitor) 放置 drag 元素事件
  • hover(item, monitor) drag 元素进入 drop 元素事件。
  • canDrop(item, monitor) 定义是否能否放置的函数。如果使用默认方式,不需要定义。
  • collect 收集函数,必须返回一个对象。具体配置参照 DropTargetMonitor,它返回的对象会出现在 index 0

DndProvider

这是一个 React 容器组件,在这个容器组件中的元素才可以实现拖放。

它一共三个 props:

  • backend 必要属性,React Dnd backend,一般都会使用 HTML5 backend
  • context backend context 配置。具体要看 backend 库的定义。
  • options backend 配置对象,具体要看 backend 库的定义。

最后

暂时就这么多,简单写了个 demo,然后了解了一下用到的 API。其实仔细了解之下还是蛮简单的。还有 useDragLayerDragPreviewImage 两个 API 没用到,但应该都不难。
明天我试着写个更加复杂的例子分享出来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 226,488评论 6 524
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 97,466评论 3 411
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 174,084评论 0 371
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,024评论 1 305
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,882评论 6 405
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,395评论 1 318
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 42,539评论 3 433
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 41,670评论 0 282
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,194评论 1 329
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,173评论 3 352
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,302评论 1 362
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,872评论 5 354
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 43,581评论 3 342
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,984评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,179评论 1 278
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,888评论 3 385
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,306评论 2 369