React DnD 的英文是 Drag and Drop for React锦担。
React DnD 是 React 和 Redux 的核心作者 Dan Abramov 創(chuàng)造的一組 React 高階組件祈坠,基于數(shù)據(jù)而不是視圖開發(fā)的一套拖拽工具,主要使用typescript進(jìn)行開發(fā)。
基礎(chǔ)概念和使用示例可以參考:http://www.reibang.com/p/8a1e16d5519b和官方tutorial畦贸。
作者基于HTML5提供了一個(gè)backend實(shí)現(xiàn)抢腐,相關(guān)api參考:HTML_Drag_and_Drop_API。
React DnD 的基本概念
Manager
Manager是DnD中非常重要的角色模叙,內(nèi)部保存了store、monitor和backend鞋屈,Manager作為入口范咨,對(duì)外暴露三者,會(huì)訂閱store厂庇,并調(diào)用Backend的setup(負(fù)責(zé)注冊(cè)拖拽相關(guān)一系列的事件)或teardown(銷毀拖拽事件)渠啊。
Backends
React DnD 抽象了后端的概念,我們可以使用 HTML5 拖拽后端权旷,也可以自定義 touch替蛉、mouse 事件模擬的后端實(shí)現(xiàn),后端主要用來(lái)抹平瀏覽器差異,處理 DOM 事件躲查,同時(shí)把 DOM 事件轉(zhuǎn)換為 React DnD 內(nèi)部的 redux action它浅。主要注冊(cè)了拖拽相關(guān)的監(jiān)聽器,對(duì)外提供了dom(drag镣煮、preview和drop)注入方法姐霍。
Item
React DnD 基于數(shù)據(jù)驅(qū)動(dòng),當(dāng)拖放發(fā)生時(shí)典唇,它用一個(gè)數(shù)據(jù)對(duì)象來(lái)描述當(dāng)前的元素镊折,比如 { cardId: 25 }。
Type
類型是唯一標(biāo)識(shí)應(yīng)用程序中整個(gè)項(xiàng)目類別的字符串(或符號(hào))介衔,類似于 redux 里面的 actions types 枚舉常量恨胚。
Monitors
拖放操作都是有狀態(tài)的,React DnD 通過(guò) Monitor 來(lái)存儲(chǔ)這些狀態(tài)并且提供查詢炎咖。
Connectors
Backend 關(guān)注 DOM 事件与纽,組件關(guān)注拖放狀態(tài),connector 可以連接組件和 Backend 塘装,可以讓 Backend 獲取到 DOM急迂。
useDrag
用于將當(dāng)前組件用作拖動(dòng)源的鉤子。
import { useDrag } from 'react-dnd' function DraggableComponent(props) { const [collectedProps, drag] = useDrag({ item: { id, type } }) return <div ref={drag}>...</div> }
useDrop
使用當(dāng)前組件作為放置目標(biāo)的鉤子蹦肴。
const [collectedProps, drop] = useDrop({ accept }) return <div ref={drop}>Drop Target</div> }
架構(gòu)與源碼
上圖說(shuō)明:
一僚碎、DndProvider
根據(jù)createDragDropManager(backend, context)創(chuàng)建名為dragDropManager的單例,并通過(guò)React.createContext共享阴幌,包含有store勺阐、monitor和backend信息;
二矛双、useDrag
2.1渊抽、通過(guò)useDragSourceMonitor生成DragSourceMonitorImpl實(shí)例monitor
1、通過(guò)useDragDropManager方法(本質(zhì)就是React.useContext)獲取dragDropManager單例议忽;
2懒闷、通過(guò)DragSourceMonitorImpl(dragDropManager)生成實(shí)例,內(nèi)部維護(hù)了一些諸如canDrag栈幸、isDragging愤估、didDrop等常用操作,本質(zhì)是通過(guò)dragDropManager單例內(nèi)部的manager實(shí)現(xiàn)速址。
2.2玩焰、通過(guò)useDragSourceConnector生成SourceConnector實(shí)例connector
1、通過(guò)useDragDropManager獲取manager芍锚,再通過(guò)manager.getBackend()獲取backend昔园;
2蔓榄、通過(guò)SourceConnector(backend)生成connector,然后將DragSourceOptions默刚、dragPreviewOptions分別保存在connector.dragSourceOptions甥郑、connector.dragPreviewOptions,并通過(guò)connector.reconnect()重連羡棵;connector內(nèi)部維護(hù)了dragSourceRef、dragSourceNode嗅钻、dragPreviewRef和dragPreviewNode等數(shù)據(jù)皂冰,還會(huì)通過(guò)調(diào)用backend.connectDragSource和backend.connectDragPreview方法,將drag和preivew相關(guān)信息傳入backend养篓。
2.3秃流、通過(guò)useRegisteredDragSource(spec, monitor, connector)注冊(cè)DragSource
1、通過(guò)DragSourceImpl(spec, monitor, connector)生成DragSource實(shí)例handler柳弄,提供了beginDrag舶胀、canDrag、isDragging和endDrag方法碧注,四個(gè)方法基本是調(diào)用spec中配置的相應(yīng)方法嚣伐;
2、調(diào)用manger的registry將handler注冊(cè)到manager中(manager.getRegitry().addSource)萍丐,生成對(duì)應(yīng)的handlerId轩端,通過(guò)monitor和connector的receiveHandlerId方法注入到各自里面。
2.4逝变、通過(guò)useCollectedProps(spec.collect, monitor, connector)返回collect數(shù)據(jù)
通過(guò)monitor.subscribeToStateChange方法注冊(cè)監(jiān)聽方法updateCollected基茵,該方法會(huì)通過(guò)collect(monitor)獲取到最新的結(jié)果
2.5、dragRef通過(guò)useConnectDragSource(connector)返回dragRef
通過(guò)執(zhí)行connector.dragSource方法獲取最新的drag ref壳影,并reconnectDragSource拱层。
2.6、通過(guò)useConnectDragPreview(connector)返回dragPreview
類似2.5宴咧。
三根灯、useDrop
四、Backend(HTML5Backend)