0

0

React DND实现的卡片排序功能(代码示例)

不言

不言

发布时间:2018-11-17 15:19:17

|

6035人浏览过

|

来源于segmentfault

转载

本篇文章给大家带来的内容是关于react dnd实现的卡片排序功能(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

在公司初学react,其中一个要求让我实现拖拽排序的功能,完成之后记录一下实现方法,采用antd和reactDND来实现这个功能。

一、环境搭建

首先,使用 create-react-app 脚手架创建一个最基本的react项目。

npm install -g create-react-app
create-react-app my-app
cd my-app

OK,构建好了react项目,然后我们引入antd,和react-dnd

$ yarn add antd
$ yarn add react-dnd
$ yarn add react-dnd-html5-backend

引用完antd后可以按照antd官网上的方法完成按需加载。

二、功能实现

我们先使用antd写出一个简单的卡片列表,修改项目目录的APP.js和App.css文件,新建一个文件CardItem.js

//App.js

import React, { Component } from 'react';
import CardItem from './CardItem'
import './App.css';

const CardList = [{ //定义卡片内容
    title:"first Card",
    id:1,
    content:"this is first Card"
  },{
    title:"second Card",
    id:2,
    content:"this is second Card"
  },{
    title:"Third Card",
    id:3,
    content:"this is Third Card"
  }
];
class App extends Component {
  state = {
    CardList
  }; 
  render() {
    return (
        
{CardList.map((item,index) => { return( ) })}
); } } export default App; //App.css .card{ display: flex; margin: 50px; } .card div{ margin-right: 20px; } //CardItem.js import React, { Component } from 'react'; import {Card} from 'antd' class CardItem extends Component{ render(){ return(

{this.props.content}

) } } export default CardItem

好了,卡片编写完成了,现在运行一下我们的项目,看一下效果

$ npm start or yarn start

990746295-5bef6f6c2c95f_articlex.png

OK,编写完成,我们现在要做的就是使用react-dnd完成卡片的拖拽排序,使得firstCard,secondCard,thirdCard可以随意的交换。

react-dnd中提供了DragDropContext,DragSource,DropTarget 3种API;

  • DragDropContext 用于包装拖拽根组件,DragSourceDropTarget 都需要包裹在DragDropContex

  • DropTarget 用于包装你需要拖动的组件,使组件能够被拖拽

  • DragSource 用于包装接收拖拽元素的组件,使组件能够放置

    DK企业网站管理系统1.1
    DK企业网站管理系统1.1

    后台路径网址+admin(请修改更安全)后台用户名密码admin产品定位:中小型企业,本系统简单实用,无冗余功能,无功能限制。后台功能:一、网站基本信息此功能允许您从后台设置网站的名称、联系人、电话、公司地址、版权、第三方统计代码等等常用信息。二、导航栏设置可以从后台调整导航栏显示项目,可以进行增加删除排序隐藏导航栏等操作三、幻灯设置可以从后台设置前台动画轮播图片,支持排序。四、单页功能用来设置公

    下载

理解了这些API的作用,一个卡片排序的构建思路大体就浮现出来了,怎么样实现一个卡片排序,其实很简单,就是把卡片列表中的每一个卡片都设置为DropTargetDragSource,最后在拖拽结束的时候进行卡片之间的重排序,完成这一功能的实现。下面我们就来一步一步的实现它。

首先设定DragDropContext,在App.js中引入 react-dndreact-dnd-html5-backend(先npm install这个插件)

//App.js

import React, { Component } from 'react';
import CardItem from './CardItem'
+ import {DragDropContext} from 'react-dnd'
+ import HTML5Backend from 'react-dnd-html5-backend'
import './App.css';

/*..
..*/

- export default App;
+ export default DragDropContext(HTML5Backend)(App);

好了,现在被App.js所包裹的子组件都可以使用DropTarget和DragSource了,我们现在在子组件CardItem中设定react-dnd使得卡片现在能够有拖动的效果。

//CardItem.js

import React, { Component } from 'react';
import {Card} from 'antd'
+ import { //引入react-dnd
    DragSource,
    DropTarget,
} from 'react-dnd'


const Types = { // 设定类型,只有DragSource和DropTarget的类型相同时,才能完成拖拽和放置
    CARD: 'CARD'
};

//DragSource相关设定
const CardSource = {  //设定DragSource的拖拽事件方法
    beginDrag(props,monitor,component){ //拖拽开始时触发的事件,必须,返回props相关对象
        return {
            index:props.index
        }
    },
    endDrag(props, monitor, component){
      //拖拽结束时的事件,可选
    },
    canDrag(props, monitor){
      //是否可以拖拽的事件。可选
    },
    isDragging(props, monitor){
      // 拖拽时触发的事件,可选
    }
};

function collect(connect,monitor) { //通过这个函数可以通过this.props获取这个函数所返回的所有属性
    return{
        connectDragSource:connect.dragSource(),
        isDragging:monitor.isDragging()
    }
}

//DropTarget相关设定
const CardTarget = {
    drop(props, monitor, component){ //组件放下时触发的事件
        //...
    },
    canDrop(props,monitor){ //组件可以被放置时触发的事件,可选
        //...
    },
    hover(props,monitor,component){ //组件在target上方时触发的事件,可选
        //...
    },
    
};

function collect1(connect,monitor) {//同DragSource的collect函数
    return{
        connectDropTarget:connect.dropTarget(),
        isOver:monitor.isOver(), //source是否在Target上方
        isOverCurrent: monitor.isOver({ shallow: true }), 
        canDrop: monitor.canDrop(),//能否被放置
        itemType: monitor.getItemType(),//获取拖拽组件type
    }
}
class CardItem extends Component{

    render(){
        const { isDragging, connectDragSource, connectDropTarget} = this.props;
        let opacity = isDragging ? 0.1 : 1; //当被拖拽时呈现透明效果

        return connectDragSource( //使用DragSource 和 DropTarget
            connectDropTarget( 

{this.props.content}

) ) } } // 使组件连接DragSource和DropTarget let flow = require('lodash.flow'); export default flow( DragSource(Types.CARD,CardSource,collect), DropTarget(Types.CARD,CardTarget,collect1) )(CardItem)

最后这个连接方法我参考了 reactDND官网 的说明,你可以去 lodash.flow的官网 进行查看并下载。
当然你也可以选择构造器的方法进行引用,如@DragSource(type, spec, collect)和@DropTarget(types, spec, collect).

Even if you don't plan to use decorators, the partial application can
still be handy, because you can combine several DragSource and
DropTarget declarations in JavaScript using a functional composition
helper such as _.flow. With decorators, you can just stack the
decorators to achieve the same effect.

import { DragSource, DropTarget } from 'react-dnd';
import flow from 'lodash/flow';

class YourComponent {
  render() {
    const { connectDragSource, connectDropTarget } = this.props
    return connectDragSource(connectDropTarget(
      /* ... */
    ))
  }
}

export default flow(
  DragSource(/* ... */),
  DropTarget(/* ... */)
)(YourComponent);

现在我们已经完成了一个拖拽效果的实现,现在我们来看一下效果

2836309455-5bef8b2e0522d_articlex.png

可以很明显的看到拖拽带来的效果,接下来我们要完成拖拽放置后的排序函数。
我们将排序函数放在App.js当中,在CardItem.js中的CardTarget构造方法中的hover函数中进行调用,接下来看具体的实现方法.

//CardItem.js

const CardTarget = {
    hover(props,monitor,component){
        if(!component) return null; //异常处理判断
        const dragIndex = monitor.getItem().index;//拖拽目标的Index
        const hoverIndex = props.index; //放置目标Index
        if(dragIndex === hoverIndex) return null;// 如果拖拽目标和放置目标相同的话,停止执行
        
        //如果不做以下处理,则卡片移动到另一个卡片上就会进行交换,下方处理使得卡片能够在跨过中心线后进行交换.
        const hoverBoundingRect = (findDOMNode(component)).getBoundingClientRect();//获取卡片的边框矩形
        const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2;//获取X轴中点
        const clientOffset = monitor.getClientOffset();//获取拖拽目标偏移量
        const hoverClientX = (clientOffset).x - hoverBoundingRect.left;
        if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) { // 从前往后放置
            return null
        }
        if (dragIndex > hoverIndex && hoverClientX > hoverMiddleX) { // 从后往前放置
            return null
        }
        props.DND(dragIndex,hoverIndex); //调用App.js中方法完成交换
        monitor.getItem().index = hoverIndex; //重新赋值index,否则会出现无限交换情况
    }
}
//App.js

    handleDND = (dragIndex,hoverIndex) => {
        let CardList = this.state.CardList;
        let tmp = CardList[dragIndex] //临时储存文件
        CardList.splice(dragIndex,1) //移除拖拽项
        CardList.splice(hoverIndex,0,tmp) //插入放置项
        this.setState({
            CardList
        })
    };
    
    
    /* ...
           */
           
    //添加传递参数传递函数
    

好了,现在我们已经完成了一个卡片排序功能的小demo,让我们来看一下效果吧!

3687378792-5befb7045bd2a_articlex.gif

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
html5动画制作有哪些制作方法
html5动画制作有哪些制作方法

html5动画制作方法有使用CSS3动画、使用JavaScript动画库、使用HTML5 Canvas等。想了解更多html5动画制作方法相关内容,可以阅读本专题下面的文章。

513

2023.10.23

HTML与HTML5的区别
HTML与HTML5的区别

HTML与HTML5的区别:1、html5支持矢量图形,html本身不支持;2、html5中可临时存储数据,html不行;3、html5新增了许多控件;4、html本身不支持音频和视频,html5支持;5、html无法处理不准确的语法,html5能够处理等等。想了解更多HTML与HTML5的相关内容,可以阅读本专题下面的文章。

439

2024.03.06

html5从入门到精通汇总
html5从入门到精通汇总

想系统掌握HTML5开发?本合集精选全网优质学习资源,涵盖免费教程、实战项目、视频课程与权威电子书,从基础语法到高级特性(Canvas、本地存储、响应式布局等)一应俱全,适合零基础小白到进阶开发者,助你高效入门并精通HTML5前端开发。

92

2025.12.30

html5新老标签汇总
html5新老标签汇总

HTML5在2026年持续优化网页语义化与交互体验,不仅引入了如<header>、<nav>、<article>、<section>、<aside>、<footer>等结构化标签,还新增了<video>、<audio>、<canvas>、<figure>、<time>、<mark>等增强多媒体与

125

2025.12.30

html5空格代码怎么写
html5空格代码怎么写

在HTML5中,空格不能直接通过键盘空格键实现,需使用特定代码。本合集详解常用空格写法:&nbsp;(不间断空格)、&ensp;(半个中文空格)、&emsp;(一个中文空格)及CSS的white-space属性等方法,帮助开发者精准控制页面排版,避免因空格失效导致布局错乱,适用于新手入门与实战参考。

78

2025.12.30

html5怎么做网站教程
html5怎么做网站教程

想从零开始学做网站?这份《HTML5怎么做网站教程》合集专为新手打造!涵盖HTML5基础语法、页面结构搭建、表单与多媒体嵌入、响应式布局及与CSS3/JavaScript协同开发等核心内容。无需编程基础,手把手教你用纯HTML5创建美观、兼容、移动端友好的现代网页。附实战案例+代码模板,快速上手,轻松迈出Web开发第一步!

158

2025.12.31

HTML5建模教程
HTML5建模教程

想快速掌握HTML5模板搭建?本合集汇集实用HTML5建模教程,从零基础入门到实战开发全覆盖!内容涵盖响应式布局、语义化标签、Canvas绘图、表单验证及移动端适配等核心技能,提供可直接复用的模板结构与代码示例。无需复杂配置,助你高效构建现代网页,轻松上手前端开发!

30

2025.12.31

html5怎么使用
html5怎么使用

想快速上手HTML5开发?本合集为你整理最实用的HTML5使用指南!涵盖HTML5基础语法、主流框架(如Bootstrap、Vue、React)集成方法,以及无需安装、直接在线编辑运行的平台推荐(如CodePen、JSFiddle)。无论你是新手还是进阶开发者,都能轻松掌握HTML5网页制作、响应式布局与交互功能开发,零配置开启高效前端编程之旅!

45

2025.12.31

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号