用 React Hooks 的方式使用 react-dnd

以前寫過一篇 react-dnd 用法的文章,裏面寫的可能比較囉嗦了,可是內容比較詳細,不少 API 都羅列了出來。目前 React Hooks 出來了,react-dnd 也作了對應的更新,因此本篇使用 React Hooks + TypeSscript 對 react-dnd 用法從新梳理一下。css

前言

當前版本介紹

  • 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 -ghtml

環境搭建

  • 建立腳手架:npx create-react-app react-dnd-hooks --typescript
  • 安裝 react-dndyarn add react-dnd
  • 安裝 react-dnd-html5-backendyarn add react-dnd-html5-backend

爲了方便統一個標識,'拖拽組件' 使用 'drag 組件' 代替,'目標接收組件' 使用 'drop 組件' 代替html5


1、用 DndProvider 將根節點包裹起來

想要使用 react-dnd 進行拖拽操做,須要用 DndProvider 標籤將根節點包裹起來,並傳入一個 backend 參數: index.tsx 文件react

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'));
複製代碼

2、讓元素能夠動起來

好比如今有一個 Box 組件,咱們想將其進行拖拽,此時咱們先聲明一下這個組件git

建立 Box 組件

Box.tsxgithub

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 組件typescript

讓 Box 組件動起來

使用 react-dnd 提供的 useDrag, 返回的第一個值是 collect 方法返回的對象(這裏沒有使用,因此省略了),返回的第二個值是一個 ref,將其賦值給想要拖拽的元素便可實現組件拖動。 Box.tsxnpm

...
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 組件,下面咱們來看看怎麼操做。app


3、建立 Dustbin 組件用來接收 drag 組件

使用 react-dnd 提供的 useDrop,返回的第一個值是 collect 方法返回的對象,返回的第二個值是一個 ref,將其賦值給想要接收的 drop 組件便可感應到 drag 組件。dom

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 組件裏使用。


4、效果圖

Dustbin 效果圖


5、其餘經常使用 API 和注意事項

  • drag 組件經常使用的屬性:

    • item:是一個對象,必需要有一個 type 屬性

    • begin(mintor: DragSourceMonitor):組件開始拖拽,必須返回一個對象包含 type 屬性,會覆蓋 item 屬性返回的對象,會被傳入 drop 組件 hover 和 drop 方法的第一個參數

    • end(item, mintor: DragSourceMonitor): 組件中止拖拽時觸發,itemdrop 組件在 drop 方法執行時返回的對象,等同於 mintor.getDropResult() 的值

  • drop 組件經常使用的屬性

    • accept:字符串,必須和對應 drag 組件的 item 屬性中的 type 值一致

    • hover(item, minoter: DropTargetMonitor)drag 組件在 drop 組件上方 hove 時觸發

    • drop(item, minoter: DropTargetMonitor)drag 組件拖拽結束後,放到 drop 組件時觸發,返回的值會做爲參數傳遞給 drag 組件 end 方法的第一個參數

  • 讓組件既能夠被拖拽也能夠接收拖拽元素

import React, { useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd'

const Card = () => {
    const ref = useRef<HTMLDivElement>(null);

    const [, drop] = useDrop({
        accept: 'Card',
    });

    const [, drag] = useDrag({
        item: { type: 'Card' }
    });

    // 使用 drag 和 drop 包裝 ref
    drag(drop(ref));

    // 將變量 ref 傳給元素的 ref 便可
    return (<div ref={ ref }>既能夠被拖動也能夠接收拖動組件</div>)
}
複製代碼
  • drag 組件想傳遞一些數據出去

    • 直接使用 item 屬性傳:
    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(),
          })
      })
    複製代碼

6、一個更復雜的 Demo 演示

一個能夠進行拖拽放置並進行拖拽排序的例子,有興趣的小夥伴能夠看看。

效果圖

拖拽並排序預覽

Demo 地址

react-dnd-hooks-demo

歡迎 Star! 歡迎 Fork!


7、資源連接

相關文章
相關標籤/搜索