react-dnd 拖拽排序

开始

最近刚刚做完自定义表单的组件,分享一下拖拽排序。

效果图

react-dnd 拖拽排序

准备工作

官方文档

https://react-dnd.github.io/react-dnd/about

安装react-dnd、react-dnd-html5-backend

npm install react-dnd react-dnd-html5-backend

开始开发

项目是基于ts的,不需要可自行删除

定义父组件

const Drag:React.FC<{}> = props => {
  return (
    <div className="drag-wrapper">
      {/* DndProvider组件提供了react-dnd的功能,必须通过backend绑定HTML5Backend*/}
      <DndProvider backend={HTML5Backend}>
        <DragSortComponent/>
      </DndProvider>
    </div>
  )
}

export default Drag

定义内容组件

const DragSortComponent:React.FC<{}> = props => {
  const [list,setList] = useState<{id:number,name:string}[]>(List)
  const [itemClass, setItemClass] = useState<{ key: number | null; value: string }>({
    key: null,
    value: '',
  })
  // 拖拽后的值
  const sortItems = useRef<{ dragRow: any; placeRow: any; posi: string }>({
    dragRow: {},
    placeRow: {},
    posi: '',
  })
  // 拖拽结束后的方法
  const onDrop = (item: any, monitor: DropTargetMonitor) => {
    const { dragRow, placeRow, posi } = sortItems.current
    let _map: any[] = JSON.parse(JSON.stringify(list))
    let index1 = _map.findIndex(v => v.id === dragRow.id) // 拖拽的itemIndex
    _map.splice(index1, 1) // 先删掉拖拽的,在获取放置的
    let index = _map.findIndex(v => v.id === placeRow.id) // 放置的itemIndex
    if (index !== -1 && index1 !== -1) {
      _map.splice(posi === 'bottom' ? index + 1 : index, 0, dragRow)
      setList(() => _map)
    }
  }
  // DragSortItemComponent组件是通用排序组件,所以需要在父组件在定义一个useDrop,来改变数据 必须挂载在父级div
  const [, drop] = useDrop({
    accept: 'sort', // 必须和拖拽的accept一致
    drop: onDrop,
    collect: monitor => ({
      isOver: monitor.isOver({ shallow: true }),
      canDrop: monitor.canDrop(),
    }),
  })
  const onItemDragClass = (key:number,value:string) => {
    if (itemClass.value !== value) {
      setItemClass(() => {
        let data = { key, value }
        return data
      })
    }
  }
  const onSortItemChange = (dragRow: any, placeRow: any, posi: string) => {
    sortItems.current = { dragRow, placeRow, posi }
  }
  return (
    <div className="drag-sort-component-wrapper" ref={drop}>
      {
        list.map(v => (
          <DragSortItemComponent
            key={v.id}
            row={v}
            onItemDragClass={onItemDragClass}
            onSortItemChange={onSortItemChange}
            keyName="id"
          >
            <div className={['drag-item ',itemClass.key === v.id ? itemClass.value : ''].join(' ')}>{v.name}</div>
          </DragSortItemComponent>
        ))
      }
    </div>
  )
}

定义拖拽排序组件

/**
 * 通用拖拽排序的容器
 * @param row 当前行
 * @param onItemDragClass 拖拽过程的样式 top | bottom
 * @param onSortItemChange 拖拽结束后返回的值 dragRow 当前拖拽 placeRow 放置的,posi 位置 top | bottom
 * @param keyName 键名
 */
type IProps = {
  row: any,
  onItemDragClass: (key:number,value:string) => void,
  onSortItemChange: (dragRow: any, placeRow: any, posi: string) => void // 排序后
  keyName: string
}
const DragSortItemComponent:React.FC<IProps> = props => {
  const { row,onItemDragClass,onSortItemChange,keyName } = props
  const ref = useRef<HTMLDivElement>(null)
  /**
   * 拖拽容器
   */
  const [, drop] = useDrop({
    // 定义拖拽的类型
    accept: 'sort',
    drop: (item, monitor) => {
      const didDrop = monitor.didDrop()
      if (didDrop) {
        return
      }
    },
    canDrop: (item, mointor) => {
      // 阻止默认拖拽释放
      onItemDragClass(row[keyName], '')
      return false
    },
    hover: (item: any, monitor) => {
      const didHover = monitor.isOver({ shallow: true })
      if (didHover) {
        // 拖拽目标的id
        const dragIndex = item[keyName]
        // 放置目标id  可以用index | id 只要是number,数据里唯一的就可以
        const hoverIndex = row[keyName]
        // 如果一样不处理
        if (dragIndex === hoverIndex) {
          onItemDragClass(row[keyName], '')
          return
        }
        // 获取放置的位置
        const hoverBoundingRect = ref.current?.getBoundingClientRect() as DOMRect
        // 获取放置的Y轴中点
        const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
        // 获取拖拽目标偏移量
        const clientOffset = monitor.getClientOffset() as XYCoord
        const hoverClientY = clientOffset.y - hoverBoundingRect.top
        if (dragIndex !== hoverIndex) {
          if (hoverMiddleY < hoverClientY) {
            onItemDragClass(row[keyName], 'bottom')
          } else {
            onItemDragClass(row[keyName], 'top')
          }
          // 如果不做成通用拖拽容器,把参数存起来,把这个放在useDrag的end方法里,
          onSortItemChange(item, row, hoverMiddleY < hoverClientY ? 'bottom' : 'top')
        }
      }
    },
    collect: monitor => ({
      isOver: monitor.isOver({ shallow: true }),
      canDrop: monitor.canDrop(),
    }),
  })
  /**
   * 定义拖拽
   * isDragging 是否拖拽
   */
  const [{ isDragging }, drag] = useDrag({
    item: { ...row, type: 'sort'},
    end: () => {
      // onSortItemChange(item, row, hoverMiddleY < hoverClientY ? 'bottom' : 'top')
    },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
      didDrop: monitor.isDragging(),
    }),
  })
  drop(drag(ref))
  return (
    <div ref={ref} style={{ opacity: isDragging ? 0 : 1 }}>
      {props.children}
    </div>
  )
}
上一篇:Java-- synchronized--原理总结--重量级锁-轻量级锁和锁膨胀


下一篇:K8s-1-交付dubbo微服务