react-dnd使用说明及列表位置切换案例

该博客展示了如何在React应用中利用react-dnd库创建一个简单的拖放列表。通过DndProvider和HTML5Backend提供拖放上下文,Item组件使用useDrag和useDrop Hooks实现元素的拖放行为,允许用户在列表中重新排列项目。当拖放操作发生时,更新列表状态以反映新的顺序。

案例

dnd.tsx

import React, { useState, useRef } from 'react'
import "./dnd.less"

// react-dnd核心
import { DndProvider } from "react-dnd"
import { HTML5Backend } from "react-dnd-html5-backend"

import Item from "./components/item"

const dnd: React.FC = (props: any) => {
  const types = {
    item: 'item',
  }

  const [list, setlist] = useState([1, 2, 3, 4])

  return (
    <div className='main'>
      {/* 只有<DndProvider backend={HTML5Backend}></DndProvider>的子孙元素才能使用 react-dnd */}
      <DndProvider backend={HTML5Backend}>
        <div className="list">
          {
            list.map((item: any, index: any) => {
              return <Item key={item} item={item} index={index} type={types.item} list={list} setList={setlist} />
            })
          }
        </div>
      </DndProvider>
    </div>
  )
}
export default dnd

components/item.tsx

import React, { useState, useRef } from 'react'

import { useDrag, useDrop } from "react-dnd"

interface ItemType {
  item: number;
  index: number;
  type: string;
  list: any;
  setList: any;
}

const dnd: React.FC<ItemType> = (props) => {
  const { item, index, type, list, setList } = props
  const style = {
    padding: '10px',
    marginBottom: '10px',
    border: '1px solid red',
    cursor: 'move',
  }

  let ref = useRef()
  // useDrop的accept和useDrag的type相同才能相互拖拽
  const [, drop] = useDrop({
    accept: type,
    collect: () => ({}),
    hover(item: any, monitor: any) {
      // 拖拽的索引
      const dragIndex = item.index
      // 放置目标的索引
      const hoverIndex = index
      // 如果拖拽目标和放置目标相同就return
      if (dragIndex === hoverIndex) return
      const { top, bottom } = ref.current.getBoundingClientRect()
      // 放置目标半的高度
      const halOfHoverHeigth = (bottom - top) / 2
      const { y } = monitor.getClientOffset()
      const hoverClientY = y - top
      // 当移动到防止目标一半时切换位置
      if (
        (dragIndex < hoverIndex && hoverClientY > halOfHoverHeigth) ||
        (dragIndex > hoverIndex && hoverClientY < halOfHoverHeigth)
      ) {
        const dragItem = list[dragIndex]
        list.splice(dragIndex, 1)
        list.splice(hoverIndex, 0, dragItem)
        setList([...list])

        item.index = hoverIndex
      }
    }
  })
  const [{ isDragging }, drag] = useDrag({
    type,
    item: () => ({ item, index }),
    collect: (monitor) => ({
      isDragging: monitor.isDragging()
    })
  })
  const opacity = isDragging ? 0.4 : 1
  drag(drop(ref))

  return (
    <div className='item' ref={ref} style={{ ...style, opacity }}>
      {item}
    </div>
  )
}
export default dnd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值