亲宝软件园·资讯

展开

react项目中使用react-dnd实现列表的拖拽排序功能

songxueing 人气:0

现在有一个新需求就是需要对一个列表,实现拖拽排序的功能,要实现的效果如下图:

可以通过 react-dnd 或者 react-beautiful-dnd 两种方式实现,今天先讲下使用react-dnd是如何实现的,github地址:

https://react-dnd.github.io/react-dnd/docs/api/dnd-provider

1.先安装依赖

npm i react-dnd

npm i react-dnd-html5-backend

2.创建一个 index.js 文件

DndProvider 组件为您的应用程序提供 React-DnD 功能。这必须通过 backend 支柱注入后端,但也可以注入 window 物体。

import React from 'react'
import Example from './example'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
	
class App extends React.Component{
    
    /**
    * 获取排序后的新数据回调函数
    */
    handlePreviewList = (previewList) => {
        this.setState({
             previewList
        })
    }
	return (
		<div className="App">
			<DndProvider backend={HTML5Backend}>
				<Example previewList={previewList} 
                    handlePreviewList={this.handlePreviewList}/>
			</DndProvider>
		</div>
	)
}
	
export default App;

3.新建example.js文件

previewList是 index.js组件传入的数据。

handlePreviewList 是保存排序后的新数据。

import React, { useState } from 'react'
import TopicList from './TopicList';
 
const Container = ({ previewList, handlePreviewList }) => {
    {
        const [topic] = useState(previewList)
        const handleDND = (dragIndex, hoverIndex) => {
            let tmp = previewList[dragIndex] //临时储存文件
            previewList.splice(dragIndex, 1) //移除拖拽项
            previewList.splice(hoverIndex, 0, tmp) //插入放置项
            handlePreviewList(previewList)
        };
        const renderCard = (item, index) => {
            return (
                <TopicList
                    key={item.questionTuid}
                    index={index}
                    id={item.questionTuid}
                    text={item.questionContent}
                    moveCard={handleDND}
                />
            )
        }
        return (
            <div>
                {
                    topic.map((item, i) => renderCard(item, i))
                }
            </div>
        )
    }
}
export default Container

4.新建TopicLis.js文件

useDrag 一个钩子,用于将当前组件用作拖动源。

参数

spec规范对象

返回值数组

规范对象成员

useDrop 一个钩子,用于将当前组件用作放置目标。

参数

spec规范对象

返回值数组

规范对象成员

import React, { useRef } from 'react'
import { useDrag, useDrop } from 'react-dnd'
import ItemTypes from './ItemTypes';
const style = {
  padding: '0.5rem 1rem',
  marginBottom: '.5rem',
  backgroundColor: 'white',
  cursor: 'move',
}
const TopicList = ({ id, text, index, moveCard }) => {
  const ref = useRef(null)
  const [, drop] = useDrop({
    //定义拖拽的类型
    accept: ItemTypes.TOPIC,    
    hover(item, monitor) {
      //异常处理判断
      if (!ref.current) { 
        return
      }
      //拖拽目标的Index
      const dragIndex = item.index;
      //放置目标Index
      const hoverIndex = index; 
      // 如果拖拽目标和放置目标相同的话,停止执行
      if (dragIndex === hoverIndex) { 
        return
      }
      //如果不做以下处理,则卡片移动到另一个卡片上就会进行交换,下方处理使得卡片能够在跨过中心线后进行交换.
      //获取卡片的边框矩形
      const hoverBoundingRect = ref.current.getBoundingClientRect();    
      //获取X轴中点
      const hoverMiddleY =
        (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; 
      //获取拖拽目标偏移量
      const clientOffset = monitor.getClientOffset();   
      const hoverClientY = clientOffset.y - hoverBoundingRect.top
      // 从上往下放置
      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {  
        return
      }
      // 从下往上放置
      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {  
        return
      }
      moveCard(dragIndex, hoverIndex);  //调用方法完成交换
      item.index = hoverIndex;  //重新赋值index,否则会出现无限交换情况
    },
  })
  const [{ isDragging }, drag] = useDrag({
    item: { type: ItemTypes.TOPIC, id, index },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const opacity = isDragging ? 0 : 1
  drag(drop(ref))
  return (
    <div ref={ref} style={{ ...style, opacity }}>
      <span style={{ float: 'left' }}>{index + 1}.</span>
      <div className='stem' dangerouslySetInnerHTML={{ __html: text }}></div>
    </div>
  )
}
export default TopicList

5.新建 ItemTypes.js

export default {
  TOPIC: 'topic'
}

注意:react的版本需要是react16的新版本,否则会报如下图所示的错误,具体兼容到几,未测试,但是之前的react 16.4.2是实现不了当前功能的,所以在开发前请确认react版本

加载全部内容

相关教程
猜你喜欢
用户评论