使用react-beautiful-dnd實現(xiàn)列表間拖拽踩坑
為什么選用react-beautiful-dnd
相比于react-dnd,react-beautiful-dnd更適用于列表之間拖拽的場景,支持移動端,且較為容易上手。
基本使用方法
基本概念
- DragDropContext:構(gòu)建一個可以拖拽的范圍
- onDragStart:拖拽開始回調(diào)
- onDragUpdate:拖拽中的回調(diào)
- onDragEnd:拖拽結(jié)束時的回調(diào)
- Droppable - 可以放置拖拽塊的區(qū)域
- Draggalbe - 可被拖拽的元素
使用方法
把你想能夠拖放的代碼放到DragDropContext中
import { DragDropContext } from 'react-beautiful-dnd';
class App extends React.Component {
onDragStart = () => {
/*...*/
};
onDragUpdate = () => {
/*...*/
}
onDragEnd = () => {
// the only one that is required
};
render() {
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragUpdate={this.onDragUpdate}
onDragEnd={this.onDragEnd}
>
<div>Hello world</div>
</DragDropContext>
);
}
}
確定可放置區(qū)域Dropppable
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
class App extends React.Component {
// ...
render() {
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragUpdate={this.onDragUpdate}
onDragEnd={this.onDragEnd}
>
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
{...provided.droppableProps}
>
<h2>I am a droppable!</h2>
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
}
- 必需的DroppableId(字符串),用于唯一標(biāo)識應(yīng)用程序的droppable。不要更改此ID特別是在拖動時
- provided.placeholder: 占位符(這個占位符是默認的,一般不咋符合需求)
- snapshot: 當(dāng)前拖動狀態(tài),可以用來在被拖動時改變Droppable的外觀
在Dropppable區(qū)域使用Draggable包裹拖拽元素
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
class App extends React.Component {
// ...
render() {
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragUpdate={this.onDragUpdate}
onDragEnd={this.onDragEnd}
>
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
{...provided.droppableProps}
>
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<h4>My draggable</h4>
</div>
)}
</Draggable>
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
);
}
}
- Draggable必須始終包含在Droppable中
- DraggablebId(字符串):必須存在唯一ID,和index(如果為遍歷 key也需要)不要更改此ID,特別是在拖動時
拖拽結(jié)束時,改變源數(shù)據(jù)
onDragEnd = result => {
const { source, destination, draggableId } = result;
if (!destination) {
return;
}
// 修改源和目標(biāo)數(shù)組,將拖拽元素從源數(shù)組中刪除,再插入到目標(biāo)數(shù)組中
this.setState({
xxx: xxx,
});
}
使用過程中遇到的問題
向拖拽的目標(biāo)區(qū)域增加自定義占位符(custom placeholder)
react-beautiful-dnd在拖拽到目標(biāo)區(qū)域時,目標(biāo)區(qū)域的元素之間會給當(dāng)前拖拽元會自動空出一段space,這段space的距離是目標(biāo)區(qū)域Draggable元素的大?。ǖ话ㄔ氐膍argin邊距,這也是一個坑,下文會說到解決方法)。
因此可以在這段距離中采用絕對定位,增加自定義占位符。具體做法:計算出當(dāng)前自定義占位符元素的left & top距離,在dragUpdate事件中更新這兩個距離,可參考beatiful-dnd-custom-placeholder-demo
拖拽時,修改拖拽元素的transform屬性,導(dǎo)致拖拽會卡死在某處,拖拽元素放置位置錯誤
在官方文檔中,有這樣一段說明, 大概是說draggable元素采用了position: fixed定位,但會受到transform會影響。
#### Warning: `position: fixed`
`react-beautiful-dnd` uses `position: fixed` to position the dragging element. This is quite robust and allows for you to have `position: relative | absolute | fixed` parents. However, unfortunately `position:fixed` is [impacted by `transform`](http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/) (such as `transform: rotate(10deg);`). This means that if you have a `transform: *` on one of the parents of a `<Draggable />` then the positioning logic will be incorrect while dragging. Lame! For most consumers this will not be an issue.
To get around this you can [reparent your <Draggable />](/docs/guides/reparenting.md). We do not enable this functionality by default as it has performance problems.
提供了如下解決方法:使用createPortal給拖動元素掛在空的父元素上,可參考issue: transform on parent messes up dragging positioning
但是這個方法并不能解決我的問題,因為還有自定義placeholder的需求。在拖拽時還需要計算placeholder的left的距離,也就需要獲取當(dāng)前拖拽元素的parentNode下的子元素,使用createPortal則獲取不到拖拽元素的原parentNode,因此放棄createPortal的方案。采用改變width和height達到transform:scale的效果。
移動端拖拽元素需要長按該元素(long-press)
官方文檔中給出的說明是,在移動端場景下,在draggable元素上的手指操作,無法確定是tap,force press,或者scroll,所以需要長按該元素才能確定是拖拽。
Starting a drag: long press
A user can start a drag by holding their finger 👇 on an element for a small period of time 🕑 (long press)
拖拽某個元素懸停在目標(biāo)位置時,空出的插入space距離不準(zhǔn)確的問題
這個就是上文中提到的,Draggable之間留的placeholder的空余距離是一個Draggable的距離,但不包括Dragglable的margin邊距,可參考這個issue。
最后采用padding來控制Draggable之間的距離,這樣在拖拽時空出的space就包括了padding。
總結(jié)
react-beautiful-dnd比較容易上手, 到2021年3月發(fā)布了v13.1.0較為活躍, 以上踩過的坑,希望對大家有所幫助。
參考資料
官網(wǎng)beautiful-dnd
react-beautiful-dnd入門教程
到此這篇關(guān)于使用react-beautiful-dnd實現(xiàn)列表間拖拽踩坑 的文章就介紹到這了,更多相關(guān)react 列表拖拽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react native實現(xiàn)往服務(wù)器上傳網(wǎng)絡(luò)圖片的實例
下面小編就為大家?guī)硪黄猺eact native實現(xiàn)往服務(wù)器上傳網(wǎng)絡(luò)圖片的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
解讀react的onClick自動觸發(fā)等相關(guān)問題
這篇文章主要介紹了解讀react的onClick自動觸發(fā)等相關(guān)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
React使用fullpage.js實現(xiàn)整屏翻頁功能
最近筆者在完成一個移動端小項目的過程中需要實現(xiàn)整屏翻頁的效果;調(diào)研完畢之后,最終決定使用pullPage.js實現(xiàn)此功能,fullPage.js使用起來比較方便,并且官網(wǎng)上也給了在react項目中使用的demo,本文記錄了這個第三方庫的使用過程,感興趣的朋友可以參考下2023-11-11
詳解React Native網(wǎng)絡(luò)請求fetch簡單封裝
本篇文章主要介紹了詳解React Native網(wǎng)絡(luò)請求fetch簡單封裝,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
React Hooks: useEffect()調(diào)用了兩次問題分析
這篇文章主要為大家介紹了React Hooks: useEffect()調(diào)用了兩次問題分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11

