大家好,我是王福鵬。
我是一名資深全端工程師,也是 17.5k 開源專案 PMP 的作者。現在我正在開發一個Notion風格的知識庫
HuashuiAI 包括 AI 寫作和協作,使用 React Nextjs 和 Supabase。
在這篇文章中,我將分享如何透過 React 和 dnd-kit 實現樹列表拖放排序。原始碼連結在本文底部。
Dnd-kit 是 React 生態中常見的拖放工具,預設支援排序。
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd} > <SortableContext items={items} strategy={verticalListSortingStrategy} > {items.map(id => <SortableItem key={id}> <p>But it can only support the one-level list. If we want to implement a multi-level nested list (or tree), we have to customize it.</p> <h2> Define state date structure </h2> <p>Modern front-end frameworks such as React Vue are data-driven views, so defining data structures first and then considering UI rendering.</p> <p>The most common data structure definition for multi-level nested lists (trees) is as follows, and virtual DOM vnode is also defined in this way.<br> </p> <pre class="brush:php;toolbar:false">const defaultItems = [ { id: 'A', children: [] }, { id: 'B', children: [ { id: 'B1', children: [] }, { id: 'B2', children: [ { id: 'B2a', children: [] }, { id: 'B2b', children: [] }, ], }, ], }, { id: 'C', children: [] }, { id: 'D', children: [ { id: 'D1', children: [] }, { id: 'D2', children: [] }, ], }, { id: 'E', children: [] }, ]
因為狀態資料結構是巢狀的,所以我首先想到的就是巢狀並一起渲染UI結構。
首先,巢狀
然後,繼續巢狀下級
運作效果如下。問題是在同一層級內允許拖放排序,但跨層級排序是不可能的,因為它不是上下文 - 這是合理的
由於巢狀不可行,因此需要將多層轉換為單級。
但需要為每個item新增祖先Ids屬性,首先是為了顯示層次結構的深度,其次是為了知道它有哪些父節點。
interface IItem { id: string ancestorIds?: string[] children?: IItem[] } function flatten(items: IItem[]): IItem[] { return items.reduce<IItem[]>((acc, item) => { acc.push(item) if (item.children) { const children = item.children.map((i) => ({ ...i, ancestorIds: [...(item.ancestorIds || []), item.id], // add ancestorIds })) acc.push(...flatten(children)) } return acc }, []) }
轉換後的渲染效果如下,現在可以拖曳排序了。不過要修改狀態排序後才會生效。
此外,我們也可以透過祖先ID的層級關係來判斷是否可以移動。父節點不能移動到子節點,否則循環會死。
例如上圖中,如果我們要將B2拖曳到B2a的位置,我們會發現B2a的祖先ID包含B2。這是不可能的,因為您無法將專案拖曳到自己的下屬專案。
為了方便操作,資料放置在Zustand全域儲存。
Dnd-kit 將拖曳的元素稱為 activeItem,而將放置的目標位置稱為 overItem。所以修改狀態資料意味著將activeItem移到overItem的位置。
如果是單關,Dnd-kit提供了一個方法arrayMove,可以直接修改。文件連結 https://docs.dndkit.com/presets/sortable
但是在多層嵌套列表(樹)中,需要自己實現,有點麻煩。核心程式碼在這裡,大家可以下載原始碼(文末)參考。
如下圖所示,將A拖曳到B下方時,A會整體移動到B的底部,而不是在B內部。
要解決這個問題,需要判斷B之後是否還有B的子元素,如果有,則將overItem賦值給其子元素
然後將目前活動元素插入items的第一個元素中。
原始碼連結在這裡 https://github.com/wangfupeng1988/react-dnd-sortable-demo
順便說一句,我正在尋找一份國際工作機會,如果你有機會,歡迎透過我的 Github 個人資料聯繫我。
以上是React dnd-kit,實作樹列表拖放排序的詳細內容。更多資訊請關注PHP中文網其他相關文章!