插件选择
react常用的拖拽排序插件,一般会推荐这几个:react-beautiful-dnd、react-dnd、dnd-kit、react-sortable-hoc,怎么选择呢react-beautiful-dnd插件可以去git上看看作者说不再维护了,并且npm仓库里面也是没有的,你去下载会报错,就不用了,react-dnd插件比较大,实现复杂的拖拽场景,就暂时不用它,剩下两个随便挑一个把,就以dnd-kit为例子。
1.安装
我们需要安装下面几个相关插件:@dnd-kit/core、@dnd-kit/sortable、@dnd-kit/utilities
他们一次的功能在于拖动,排序,以及样式,下面我们从代码中可以看出来。
pnpm install @dnd-kit/core
2.使用场景和代码
一个简单的场景,一组div的列,可以拖动排序,下面是写成的组件代码:
1)组件代码
import { injectIntl } from "react-intl";
import { DndContext, closestCenter } from '@dnd-kit/core';
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { useState } from "react";
import { CSS } from '@dnd-kit/utilities';
import './my-dnd.less'
interface props_type {
items: any,
onDragEnd: any,
renderItem: any,
prefix: any,
className: string
}
const DraggableRow = ({ id, render, item, prefix, className, activeId }: any) => {
const { attributes, listeners, transform, setNodeRef, transition } = useSortable({ id });
const isDragging = id === activeId;
const style = {
transform: CSS.Transform.toString(transform),
transition,
background: isDragging ? 'linear-gradient(rgba(0, 140, 214, 0.1), rgba(0, 110, 255, 0.1)), #ffffff' : ''
};
return (
<div ref={setNodeRef} style={style} className={`${className} dnd-item`}>
<div {...attributes} {...listeners} className="drag-icon">
{prefix}
</div>
{render(item)}
</div>
);
};
const MyDnd: React.FC<any> = (props: props_type) => {
const { items, onDragEnd, renderItem, prefix, className } = props;
const [currentItems, setCurrentItems] = useState(items);
const [activeId, setActiveId] = useState(null);
const handleDragEnd = (event: any) => {
const { active, over } = event;
if (active.id !== over.id) {
const oldIndex = currentItems.findIndex((item: any) => item.id === active.id);
const newIndex = currentItems.findIndex((item: any) => item.id === over.id);
const newItems = arrayMove(currentItems, oldIndex, newIndex);
setCurrentItems(newItems);
onDragEnd && onDragEnd(newItems);
}
setActiveId(null);
};
return (
<DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd} onDragStart={(e: any) => setActiveId(e.active.id)}>
<SortableContext items={currentItems.map((item: any) => item.id)} strategy={verticalListSortingStrategy}>
{currentItems.map((item: any) => (
<DraggableRow
key={item.id}
id={item.id}
item={item}
render={renderItem}
prefix={prefix}
className={className}
activeId={activeId}
/>
))}
</SortableContext>
</DndContext>
);
}
export default injectIntl(MyDnd) as any;
2)如何使用
import MyDnd from "./my-dnd";
<MyDnd
items={list}
onDragEnd={(newItems: any) => set_list(newItems)}
renderItem={renderRow}
prefix={icon}
className={'file-item'}
/>
3.代码分析
1)MyDnd传参
先从使用的地方看,items是需要传入到SortableContext中进行排序的源数据,onDragEnd是触发完成排序后的回调函数,返回的是新的数据,renderItem是你自己定义的页面的样式每一行的,perfix是自定义每一行前面的icon,这里我是穿了个按钮进去,并把拖拽的动作挂载(listeners,随后会介绍)在这个按钮上的。
2)MyDnd代码
然后就进入组件的代码:DndContext
:将整个应用包裹在DndContext
中,并设置 collisionDetection
为 closestCenter
(检测最近的元素碰撞),SortableContext
:SortableContext
包裹所有可排序项,设置 strategy
为 verticalListSortingStrategy
(垂直列表的排序策略)。中间就是你要加载的自己的内容啦,就是renderItem,useSortable
提供了处理单个元素拖拽的基本接口。
3)样式
@dnd-kit/utilities主要提供了拖动时触发的样式效果。
大概就是这样,具体的API可以再具体查询。