大家好,我是王福鹏。
我是一名高级全栈工程师,也是 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中文网其他相关文章!