react 基于 dnd-kit 封装的拖拽排序组件

dnd-kit使用方法

  • 官网地址 https://docs.dndkit.com/introduction/installation

  • 安装依赖

npm install @dnd-kit/core
npm install @dnd-kit/sortable
  • 简单使用

建议直接看官网,已经描述得很详细了:https://docs.dndkit.com/presets/sortable

封装成组件使用

效果展示
type dataType = {
  id: string
  title: string
}
export default () => {
  const [list, setList] = useState<dataType[]>([])
  useEffect(() => {
    getData()
  }, [])

  const getData = async () => {
    setTimeout(() => {
      const data = new Array(10).fill('标题-').map((s,i) => (
        {id: 'id-' + i, title: s + i}
      ))
      setList(data)
    }, 200);
  }

  const onDragEnd = (list: dataType[], ids: string[]) => {
    setList(list)
  }

  return (
    <div >
      <SortListDndKit list={list} onDragEnd={onDragEnd}>
        {list.map((item) => (
          <SortItemDndKit key={item.id} id={item.id}>
            <div>
              <h4>{item.title}</h4>
            </div>
          </SortItemDndKit>
        ))}
      </SortListDndKit>
    </div>
  )
}
注意事项
  1. 如果传入的是一个函数式组件,需要用一个html元素包裹住
<SortListDndKit list={list} onDragEnd={onDragEnd}>
{list.map((item) => (
  <SortItemDndKit>
    {/* 这里需要用html元素包裹一下 */}
    <div>
      <Text title='测试组件' />
    </div>
  </SortItemDndKit> 
))}
</SortListDndKit>
  1. 这里的排序默认是读取 list 中的 id 作为 key 值的,如果 key 值是其他,可以指定 list 中传入的 idKey
{/* newId:list中的id名称 */}
<SortListDndKit idKey="newId" list={list} onDragEnd={onDragEnd}></SortListDndKit>
  1. 如果内部的元素绑定了鼠标点击之类的事件,需要先阻止事件冒泡 onMouseDown={e => e.stopPropagation()}
<SortListDndKit list={list} onDragEnd={onDragEnd}>
{list.map((item) => (
  <SortItemDndKit>
    {/* 这里需要用html元素包裹一下 */}
    <div>
      <h4>这里有很多内容</h4>
      {/* 这里需要阻止鼠标点击事件的冒泡 */}
      <button onMouseDown={e => e.stopPropagation()} onClick={()=>console.log('点击了')} >点击</button>
    </div>
  </SortItemDndKit> 
))}
</SortListDndKit>
组件源码
import React, { ReactNode } from 'react';
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragEndEvent,
  MouseSensor,
} from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

type PropsType = {
  /** 需要传递的列表 */
  list: any[];
  /** list的key值,默认是id */
  idKey?: string;
  children: ReactNode;
  /** 拖拽结束的回调 */
  onDragEnd: (arr: any[], ids: string[]) => void;
};

/** 参考官网:https://docs.dndkit.com/presets/sortable */
/** 
 * 列表排序
 */
export const SortListDndKit = ({
  list = [],
  idKey = 'id',
  children,
  onDragEnd,
}: PropsType) => {
  // 指定传感器,默认是全部
  const sensors = useSensors(
    // useSensor(PointerSensor),
    useSensor(MouseSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    }),
  );
  // 拖拽结束
  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event;
    if (active.id !== over.id) {
      const oldIndex = list.findIndex((item) => item[idKey] === active.id);
      const newIndex = list.findIndex((item) => item[idKey] === over.id);
      const ids: string[] = list.map((item) => item[idKey]);
      [ids[newIndex], ids[oldIndex]] = [ids[oldIndex], ids[newIndex]];
      const _val = arrayMove(list, oldIndex, newIndex);
      onDragEnd(_val, ids);
    }
  };

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={handleDragEnd}
    >
      <SortableContext items={list.map((item) => item[idKey])}>
        {children}
      </SortableContext>
    </DndContext>
  );
};

type SortItemType = {
  id: string;
  children: ReactNode;
};

/**
 * 列表排序的子项
 * - 函数式组件作为children,需要用html元素包裹住
 * - 例:
 * - \<SortItemDndKit>
 * -   \<div>\<Text title='测试组件' />\</div>
 * - \</SortItemDndKit> 
 */
export const SortItemDndKit = ({ id, children }: SortItemType) => {
  const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id });
  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };
  const newChild = React.Children.map(children, (child) => {
    if (!React.isValidElement(child)) {
      return null;
    }
    const childProps = {
      ...child.props,
      ref: setNodeRef,
      style,
      ...attributes,
      ...listeners,
    };
    return React.cloneElement(child, childProps);
  });
  return <>{newChild}</>;
};

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
React-dnd 排序是一种常见的前端交互方式,可以通过以下代码实现:1. 首先需要安装 react-dnd 和 react-dnd-html5-backend 两个库:``` npm install --save react-dnd react-dnd-html5-backend ```2. 在组件中引入 DragDropContext、Droppable 和 Draggable 组件:``` import { DragDropContext, Droppable, Draggable } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; ```3. 定义一个数组作为列表的数据源:``` const items = [ { id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }, { id: 3, name: 'Item 3' }, { id: 4, name: 'Item 4' }, { id: 5, name: 'Item 5' }, ]; ```4. 在组件中使用 DragDropContext 组件包裹整个列表,并在其中使用 Droppable 组件包裹每个项:``` <DragDropContext backend={HTML5Backend}> <Droppable droppableId="items"> {(provided) => ( <ul {...provided.droppableProps} ref={provided.innerRef}> {items.map((item, index) => ( <Draggable key={item.id} draggableId={item.id.toString()} index={index}> {(provided) => ( <li {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} > {item.name} </li> )} </Draggable> ))} {provided.placeholder} </ul> )} </Droppable> </DragDropContext> ```5. 在 Draggable 组件中使用 provided.draggableProps 和 provided.dragHandleProps 属性来实现功能,同时使用 provided.innerRef 属性来获取元素的引用。6. 在 Droppable 组件中使用 provided.droppableProps 和 provided.innerRef 属性来实现排序功能。7. 最后,需要在 DragDropContext 组件中定义 onDragEnd 回调函数来处理结束后的逻辑:``` function onDragEnd(result) { if (!result.destination) { return; } const newItems = Array.from(items); const [reorderedItem] = newItems.splice(result.source.index, 1); newItems.splice(result.destination.index, , reorderedItem); setItems(newItems); }<DragDropContext backend={HTML5Backend} onDragEnd={onDragEnd}> ... </DragDropContext> ```8. 在 onDragEnd 回调函数中,首先判断是否有目标位置,如果没有则直接返回。然后使用 Array.from 方法复制一份原始数据源,从中取出被的元素并删除,再将其插入到目标位置中,最后使用 setItems 函数更新数据源。以上就是 react-dnd 排序的代码实现。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值