這個17k star的拖拽庫有點(diǎn)料
感謝關(guān)注
一、背景
近期在工作中遇到了一個新的需求,該需求需要實(shí)現(xiàn)某個組件的拖拽,面對這個需求的第一個項(xiàng)目肯定是問問度娘和用最大同性交友網(wǎng)站進(jìn)行搜索,最終皇天不負(fù)有心人,讓我找到了這個17k star的React拖拽庫——React DnD。

二、簡單使用
2.1 安裝
npm?install?react-dnd?-S?//?react-dnd包,其核心包
npm?install?react-dnd-html5-backend?-S?//?拖拽的底層實(shí)現(xiàn)所需要的庫
2.2 三個核心點(diǎn)
通過使用React DnD這個庫,我認(rèn)為里面最有用的部分包含一個組件和兩個Hook API,它們分別是:
DndProvider組件 useDrag函數(shù) useDrop函數(shù)
2.2.1 DndProvider組件
如果想讓某一內(nèi)容使用React DnD的能力,需要將該部分用DndProvider進(jìn)行包裹,其接收參數(shù)如下所示:
backend:必填。一個React DnD后端。目前官方文檔有三個,分別為:react-dnd-html5-backend、react-dnd-touch-backend、react-dnd-test-backend,但是常用的還是react-dnd-html5-backend。context:可選的。用于配置后端的后端上下文。這取決于后端實(shí)現(xiàn)。options:可選的。用于配置后端的選項(xiàng)對象。這取決于后端實(shí)現(xiàn)。
下面來一起看看該組件的簡單使用:
import?{DndProvider}?from?'react-dnd';
import?{HTML5Backend}?from?'react-dnd-html5-backend';
function?App()?{
??return?(
????<div?className="App">
??????<DndProvider?backend={HTML5Backend}>
????????此處將放拖拽相關(guān)內(nèi)容
??????DndProvider>
????div>
??);
}
export?default?App;
2.2.2 useDrag函數(shù)
既然知道了整個操縱空間,接下來需要了解的就是從什么位置進(jìn)行拖拽,該庫提供了useDrag hook API,該元素可以讓一個DOM元素實(shí)現(xiàn)拖拽效果。
參數(shù)
(1) spec:創(chuàng)建規(guī)范對象的規(guī)范對象或函數(shù),其詳細(xì)內(nèi)容如下所示:
1)type
必須,是一個字符串或Symbol,只有drop和此值相同才可以進(jìn)行放置;
2)item
必須,用于描述被拖動的數(shù)據(jù)
3)previewOptions
可選的,一個簡單對象,用于描述拖動預(yù)覽選項(xiàng);
4)options
可選的,一個簡單對象
5)end(item, monitor)
可選的,當(dāng)拖拽停止,該函數(shù)被調(diào)用;
6)canDrag(monitor)
可選的,使用它指定當(dāng)前是否允許拖動;
7)isDragging(monitor)
可選的,默認(rèn)情況下,只有啟動拖動操作的拖動源才被視為拖動;
8)collect
可選的,監(jiān)聽功能
返回值
返回值是一個數(shù)組,數(shù)組內(nèi)容分別為:
collected:一個對象,包含從collect函數(shù)收集的屬性,如果collect未定義函數(shù),則返回一個空對象;drag:拖動器的連接器功能,必須附加到DOM的可拖動部分;dragPreview:用于拖動預(yù)覽的連接器功能,可以附加到DOM的預(yù)覽部分;
與拖動部分建立連接
通過ref屬性,將drag或dragPreview綁定到拖拽源上。
下面一起來看看useDrag部分的使用
import?{useDrag}?from?'react-dnd';
const?SourceBox?=?props?=>?{
????const?{children}?=?props;
????/**
?????*?返回的參數(shù)
?????* collected:一個對象,包含從collect函數(shù)收集的屬性,如果collect未定義函數(shù),則返回一個空對象
?????* drag:拖動器的連接器功能,必須附加到DOM的可拖動部分
?????*?dragPreview:用于拖動預(yù)覽的連接器功能,可以附加到DOM的預(yù)覽部分
?????*/
????const?[collected,?drag,?dragPreview]?=?useDrag({
????????//?只有drop和此值相同才可以進(jìn)行放置
????????type:?'box',
????????//?描述要拖動的數(shù)據(jù)
????????item:?{
????????????detail:?'我是可以拖動的數(shù)據(jù)!!!'
????????},
????????//?拖動停止的手end將會被調(diào)用
????????end:?(item,?monitor)?=>?{
????????????//?getDropResult()獲取釋放后的結(jié)果
????????????console.log('monitor.getDropResult():',?monitor.getDropResult());
????????????//?source是否已經(jīng)drop在target
????????????console.log('monitor.didDrop()',?monitor.didDrop());
????????},
????????//?指定當(dāng)前是否允許拖動,默認(rèn)允許
????????canDrag:?monitor?=>?{
????????????return?true;
????????},
????????//?監(jiān)聽功能
????????collect:?(monitor,?props)?=>?{
????????????return?{
????????????????isDragging:?monitor.isDragging()
????????????};
????????}
????});
????return?(
????????<div?ref={drag}>
????????????{children}
????????div>
????);
};
export?default?SourceBox;
2.2.3 useDrop函數(shù)
為了將內(nèi)容放置到目標(biāo)位置,提供了useDrop函數(shù),如下所示:
參數(shù)
(1) spec:創(chuàng)建規(guī)范對象的規(guī)范對象或函數(shù),其詳細(xì)內(nèi)容如下所示:
1)accept
必須,一個字符串,此放置目標(biāo)將僅對于指定類型的拖動源產(chǎn)生的項(xiàng)目作出反應(yīng);
2)options
可選的,一個普通的對象;
3)drop(item,monitor)
可選的,當(dāng)兼容項(xiàng)目放在目標(biāo)時被調(diào)用;
4)hover(item,monitor)
可選的,將項(xiàng)目懸停在組件時調(diào)用;
5)canDrop(item,monitor)
可選的,用它來指定放置目標(biāo)是否接受該拖拽內(nèi)容;
6)collect
可選的,監(jiān)聽功能
返回值
返回值是一個數(shù)組,數(shù)組內(nèi)容分別為:
collected:一個對象,包含從collect函數(shù)收集的屬性,如果collect未定義函數(shù),則返回一個空對象;drop:一個用于放置目標(biāo)的連接器函數(shù),必須附加到DOM的放置部分;
與放置部分建立連接
通過ref屬性,將drop與放置部分建立連接。
下面一起來看看useDrop部分的使用
import?{useDrop}?from?"react-dnd";
const?TargetBox?=?()?=>?{
????const?[collected,?drop]?=?useDrop({
????????//??此放置目標(biāo)將僅對于指定類型的拖動源產(chǎn)生的項(xiàng)目作出反應(yīng)
????????accept:?'box',
????????//?當(dāng)兼容項(xiàng)目放在目標(biāo)時調(diào)用
????????drop:?(item,?monitor)?=>?{
????????????console.log('我已經(jīng)被放到目標(biāo)!!!')
????????},
????????//?監(jiān)聽功能
????????collect:?monitor?=>?{
????????????return?{
????????????????//?是否重疊
????????????????isOver:?monitor.isOver(),
????????????????//?是否可以放置
????????????????canDrop:?monitor.canDrop(),
????????????????item:?monitor.getItem(),
????????????????didDrop:?monitor.didDrop()
????????????};
????????}
????});
????return?(
????????<div?ref={drop}>
????????????<div?className="targetBox">
????????????????這是放置的區(qū)塊
????????????div>
????????div>
????);
};
export?default?TargetBox;
2.3 monitor詳細(xì)內(nèi)容
useDrag和useDrop上掛載了很多選項(xiàng),這些選項(xiàng)中很多存在monitor對象,該對象上掛載了很多方法,下面就簡要概述幾個主要方法,如下所示:
drag上的monitor上的方法
2. drop上的monitor上的方法

三、效果圖
拖拽前

拖拽中
拖拽中拖拽的內(nèi)容跟隨鼠標(biāo)移動

拖拽后
拖拽釋放鼠標(biāo)后,一些內(nèi)容被打印出來,打印的結(jié)果是先輸出drop中的內(nèi)容再輸出end中的內(nèi)容,所以我們想做一些處理最后在SourceBox中進(jìn)行處理,如果在drop中改變React相關(guān)的數(shù)據(jù)會報(bào)錯。

四、學(xué)習(xí)感悟
這個庫的資料千篇一律,在使用過程中遇到了一些坑,接下來與各位老鐵分享一下這些坑,防止后續(xù)深陷其中。
end方法的調(diào)用時機(jī)晚于drop的調(diào)用時機(jī),所以只有在end中做釋放后的數(shù)據(jù)處理才能保證系統(tǒng)的正確性,如果在drop中就更新state或React redux中數(shù)據(jù),會引發(fā)錯誤; item數(shù)據(jù)是從Drag到Drop之間的橋梁,在drag中定義的item數(shù)據(jù)可以通過monitor.getItem()獲取; drop回調(diào)的返回值是從Drop到Drag之間的橋梁,在end中可以通過monitor.getDropResult()其返回值; 一些掛載在monitor上的位置函數(shù)并不一定適用于所有的場景,需要引入DOM相關(guān)的位置操作。
往期干貨:
?26個經(jīng)典微信小程序+35套微信小程序源碼+微信小程序合集源碼下載(免費(fèi))
?干貨~~~2021最新前端學(xué)習(xí)視頻~~速度領(lǐng)取
?前端書籍-前端290本高清pdf電子書打包下載
點(diǎn)贊和在看就是最大的支持??
