以前寫過一篇 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 -g
html
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 組件' 代替html5
想要使用 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'));
複製代碼
好比如今有一個 Box 組件,咱們想將其進行拖拽,此時咱們先聲明一下這個組件git
Box.tsx
github
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
使用 react-dnd
提供的 useDrag
, 返回的第一個值是 collect 方法返回的對象(這裏沒有使用,因此省略了),返回的第二個值是一個 ref,將其賦值給想要拖拽的元素便可實現組件拖動。 Box.tsx
npm
...
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
使用 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 組件裏使用。
drag
組件經常使用的屬性:
item
:是一個對象,必需要有一個 type
屬性
begin(mintor: DragSourceMonitor)
:組件開始拖拽,必須返回一個對象包含 type
屬性,會覆蓋 item
屬性返回的對象,會被傳入 drop 組件 hover 和 drop 方法的第一個參數
end(item, mintor: DragSourceMonitor)
: 組件中止拖拽時觸發,item
是 drop
組件在 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
組件想傳遞一些數據出去
const [, drag] = useDrag({
item: { type: 'Card', id: 1, name: 'card1', kind: 'Card }
});
複製代碼
注意:begin 方法的返回值會將 item 屬性覆蓋,因此必定要傳 type 屬性
const [, drag] = useDrag({
item: { type: 'Card' },
begin(mintor: DragSourceMonitor) {
return { type: 'Card', id: 1, name: 'card1', kind: 'Card }
}
});
複製代碼
dropTarget
接收組件中就能夠在 hover 或者 drop 方法的第一個參數中獲取到,或者使用 DropTargetMonitor
的 getItem()
函數獲取。想要獲取到 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(),
})
})
複製代碼
一個能夠進行拖拽放置並進行拖拽排序的例子,有興趣的小夥伴能夠看看。
歡迎 Star! 歡迎 Fork!