·  阅读 18331

之前写过一篇 react-dnd 用法的文章,里面写的可能比较啰嗦了,但是内容比较详细,很多 API 都罗列了出来。目前 React Hooks 出来了,react-dnd 也做了对应的更新,所以本篇使用 React Hooks + TypeSscript 对 react-dnd 用法重新梳理一下。

当前版本介绍

  • react : 16.9.0
  • react-dom : 16.9.0
  • typescript : 3.5.3
  • react-dnd : 9.3.4
  • react-dnd-html5-backend : 9.3.4
  • 脚手架使用的 create-react-app ,全局安装 create-react-app 的命令: npm i create-react-app -g

  • 创建脚手架: npx create-react-app react-dnd-hooks --typescript
  • 安装 react-dnd yarn add react-dnd
  • 安装 react-dnd-html5-backend yarn add react-dnd-html5-backend
  • 为了方便统一个标识,'拖拽组件' 使用 'drag 组件' 代替,'目标接收组件' 使用 'drop 组件' 代替

    一、用 DndProvider 将根节点包裹起来

    想要使用 react-dnd 进行拖拽操作,需要用 DndProvider 标签将根节点包裹起来,并传入一个 backend 参数: index.tsx 文件

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { DndProvider } from 'react-dnd';
    import HTMLBackend from 'react-dnd-html5-backend'
    import './index.css';
    import App from './App';
    ReactDOM.render(
        <DndProvider backend={ HTMLBackend }>
            <App />
        </DndProvider>,
        document.getElementById('root'));
    复制代码

    二、让元素可以动起来

    比如现在有一个 Box 组件,我们想将其进行拖拽,此时我们先声明一下这个组件

    创建 Box 组件

    Box.tsx

    import React, { CSSProperties } from 'react';
    const style: CSSProperties = {
        width: 200,
        height: 50,
        lineHeight: '50px',
        background: 'pink',
        margin: '30px auto'
    const Box = () => {
        return (
            <div style={ style }>可拖拽组件 Box</div>
    export default Box;
    复制代码

    这个时候我们使用鼠标按住 Box 组件,发现还没有办法拖拽 Box 组件

    让 Box 组件动起来

    使用 react-dnd 提供的 useDrag , 返回的第一个值是 collect 方法返回的对象(这里没有使用,所以省略了),返回的第二个值是一个 ref,将其赋值给想要拖拽的元素即可实现组件拖动。 Box.tsx

    import { useDrag } from 'react-dnd' ; const Box = ( ) => { // 使用 useDrag const [, drager] = useDrag ({ item : { type : 'Box' } return ( // 将第二个参数赋值给 ref < div ref = { drager } style = { style }> 可拖拽组件 Box </ div > export default Box ; 复制代码

    这个时候我们就可以使用鼠标来回拖拽 Box 组件了,但是只能拖拽,还没有地方能够接收感应到 Box 组件,下面我们来看看怎么操作。

    三、创建 Dustbin 组件用来接收 drag 组件

    使用 react-dnd 提供的 useDrop ,返回的第一个值是 collect 方法返回的对象,返回的第二个值是一个 ref,将其赋值给想要接收的 drop 组件即可感应到 drag 组件。

    Dustbin 组件:

    import React, { CSSProperties } from 'react';
    import { useDrop, DropTargetMonitor } from 'react-dnd';
    const style: CSSProperties = {
        width: 400,
        height: 400,
        margin: '100px auto',
        lineHeight: '60px',
        border: '1px dashed black'
    const Dustbin = () => {
        // 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
        const [collectProps, droper] = useDrop({
            // accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
            accept: 'Box',
            // collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
            collect: (minoter: DropTargetMonitor) => ({
                isOver: minoter.isOver()
        const bg = collectProps.isOver ? 'deeppink' : 'white';
        const content = collectProps.isOver ? '快松开,放到碗里来' : '将 Box 组件拖动到这里'
        return (
            // 将 droper 赋值给对应元素的 ref
            <div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
    export default Dustbin;
    

    温馨提示:记得将 Box 和 Dustbin 组件引用到 App.tsx 组件里使用。

    四、效果图

    五、其他常用 API 和注意事项

    drag 组件常用的属性:

    item:是一个对象,必须要有一个 type 属性

    begin(mintor: DragSourceMonitor):组件开始拖拽,必须返回一个对象包含 type 属性,会覆盖 item 属性返回的对象,会被传入 drop 组件 hover 和 drop 方法的第一个参数

    end(item, mintor: DragSourceMonitor): 组件停止拖拽时触发,itemdrop 组件在 drop 方法执行时返回的对象,等同于 mintor.getDropResult() 的值

      const [, drag] = useDrag({
          item: { type: 'Card', id: 1, name: 'card1', kind: 'Card }
    
  • 利用 begin 方法传值:
  • 注意:begin 方法的返回值会将 item 属性覆盖,所以一定要传 type 属性

      const [, drag] = useDrag({
          item: { type: 'Card' },
          begin(mintor: DragSourceMonitor) {
              return { type: 'Card', id: 1, name: 'card1', kind: 'Card }
    
  • 之后在 dropTarget 接收组件中就可以在 hover 或者 drop 方法的第一个参数中获取到,或者使用 DropTargetMonitorgetItem() 函数获取。
  • 想要获取到 drag 组件或者 drop 组件的一些状态信息

  • drag 组件:
  •   // collect 函数返回的对象会赋给 useDrop 的第一个参数 collectProps,可以在组件中直接进行使用
      const [collectProps, drag] = useDrag({
          item: { type: 'Card', id: 1, name: 'card1', kind: 'Card },
          collect: (minoter: DropTargetMonitor) => ({
              isOver: minoter.isOver(),
    
  • drop 组件:
  •   // collect 函数返回的对象会赋给 useDrop 的第一个参数 collectProps,可以在组件中直接进行使用
      const [collectProps, droper] = useDrop({
          accept: 'Box',
          collect: (minoter: DropTargetMonitor) => ({
              isOver: minoter.isOver(),
    
  • 更多状态信息的 API 可以查看我之前文章 中的 DragSourceMonitor 相关 APIDropTargetMonitor 相关 API
  •