React-DnD 拖拽实现教程 – wiki基地

React-DnD 拖拽实现教程

在现代前端应用中,拖拽(Drag and Drop)功能是提升用户体验的重要交互方式。无论是文件上传、列表排序还是看板任务管理,拖拽都能让操作变得直观高效。本文将详细介绍如何使用 React-DnD 库在 React 应用中实现拖拽功能。

1. 什么是 React-DnD?

React-DnD 是一个用于构建复杂拖拽界面的 React 工具集。它基于 HTML5 拖拽 API,但抽象了其复杂性,提供了一套更符合 React 理念的 API。React-DnD 采用了高阶组件(HOCs)和 hooks 的方式,让开发者能够声明式地定义可拖拽(Draggable)和可放置(Droppable)的组件。

核心概念:

  • DragSource (拖拽源): 可以被拖拽的组件。
  • DropTarget (放置目标): 可以接收拖拽项的区域。
  • DragItem (拖拽项): 一个简单的 JavaScript 对象,描述了被拖拽的数据。
  • Monitor (监听器): 提供拖拽状态信息,例如当前被拖拽的项、拖拽是否正在进行等。
  • Connector (连接器): 用于将组件的 DOM 节点连接到 React-DnD 的后端。

2. 环境搭建

首先,确保你有一个 React 项目。如果还没有,可以通过 Create React App 快速创建一个:

bash
npx create-react-app my-drag-app
cd my-drag-app

接下来,安装 React-DnD 及其必要的后端(通常是 HTML5 后端):

“`bash
npm install react-dnd react-dnd-html5-backend

或者 yarn add react-dnd react-dnd-html5-backend

“`

3. 项目结构

我们将创建一个简单的示例:一个包含多个可拖拽卡片的列表,可以拖拽卡片到其他位置进行排序。

src/
├── App.js
├── Card.js
└── index.js

4. 实现步骤

步骤 1: 包装根组件与 DnDProvider

src/index.jssrc/App.js 中,你需要使用 DnDProvider 包裹你的整个应用,并传入一个后端。这将使得所有子组件都能访问到拖拽上下文。

“`jsx
// src/App.js
import React from ‘react’;
import { DndProvider } from ‘react-dnd’;
import { HTML5Backend } from ‘react-dnd-html5-backend’;
import Container from ‘./Container’; // 我们稍后创建

function App() {
return (

React-DnD 拖拽示例


);
}

export default App;
“`

步骤 2: 创建可拖拽组件 (Card.js)

Card 组件将是可拖拽的项。我们将使用 useDrag hook 来使其具有拖拽能力。

“`jsx
// src/Card.js
import React, { useRef } from ‘react’;
import { useDrag, useDrop } from ‘react-dnd’;
import ‘./Card.css’; // 我们稍后创建样式

const ItemTypes = {
CARD: ‘card’,
};

function Card({ id, text, index, moveCard }) {
const ref = useRef(null);

// useDrop hook: 使卡片成为一个放置目标,以便它可以接收其他卡片
const [, drop] = useDrop({
accept: ItemTypes.CARD,
hover(item, monitor) {
if (!ref.current) {
return;
}
const dragIndex = item.index;
const hoverIndex = index;

  // 不在同一位置拖拽
  if (dragIndex === hoverIndex) {
    return;
  }

  // 确定屏幕上的矩形
  const hoverBoundingRect = ref.current?.getBoundingClientRect();

  // 获取中间垂直线
  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);

  // 注意: 这里修改了拖拽项的 index,是为了在后续的 hover 事件中保持正确性
  item.index = hoverIndex;
},

});

// useDrag hook: 使卡片成为一个拖拽源
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.CARD,
item: () => ({ id, index }), // 拖拽时携带的数据
collect: (monitor) => ({
isDragging: monitor.isDragging(), // 收集拖拽状态
}),
});

const opacity = isDragging ? 0 : 1; // 拖拽时隐藏原卡片

// 将 ref 连接到 DOM 节点,使 DnD 能够跟踪它
drag(drop(ref));

return (

{text}

);
}

export default Card;
“`

css
/* src/Card.css */
.card {
border: 1px solid gray;
padding: 0.5rem 1rem;
margin-bottom: 0.5rem;
background-color: white;
cursor: move;
}

步骤 3: 创建容器组件 (Container.js)

Container 组件将渲染多个 Card 组件,并处理卡片的移动逻辑。

“`jsx
// src/Container.js
import React, { useState, useCallback } from ‘react’;
import Card from ‘./Card’;

const initialCards = [
{ id: 1, text: ‘学习 React’ },
{ id: 2, text: ‘学习 Redux’ },
{ id: 3, text: ‘学习 React-Router’ },
{ id: 4, text: ‘学习 React-DnD’ },
];

function Container() {
const [cards, setCards] = useState(initialCards);

// useCallback 用于优化性能,避免 moveCard 函数在每次渲染时都重新创建
const moveCard = useCallback((dragIndex, hoverIndex) => {
const dragCard = cards[dragIndex];
const newCards = […cards];
// 移除拖拽的卡片
newCards.splice(dragIndex, 1);
// 在目标位置插入拖拽的卡片
newCards.splice(hoverIndex, 0, dragCard);
setCards(newCards);
}, [cards]);

return (

{cards.map((card, index) => (

))}

);
}

export default Container;
“`

css
/* src/index.css 或者 App.css */
.container {
max-width: 400px;
margin: 2rem auto;
padding: 1rem;
border: 1px dashed #ccc;
background-color: #f9f9f9;
}

5. 运行项目

“`bash
npm start

或者 yarn start

“`

现在,你可以在浏览器中看到一个卡片列表,并且可以拖拽卡片来改变它们的顺序。

6. 总结

通过上述步骤,我们成功地使用 React-DnD 实现了拖拽排序功能。React-DnD 提供了强大的抽象,让开发者能够专注于业务逻辑,而不是底层复杂的 HTML5 拖拽 API。

关键点回顾:

  • DndProvider: 提供拖拽上下文。
  • HTML5Backend: 默认的 HTML5 后端。
  • useDrag: 创建可拖拽组件的 hook。
  • useDrop: 创建放置目标的 hook。
  • item: 拖拽时传递的数据。
  • collect: 收集拖拽状态。
  • hover: 放置目标上的鼠标悬停事件,用于实现实时排序。
  • monitor: 提供了关于当前拖拽操作的有用信息。
  • refconnector: 用于连接 DOM 元素到 React-DnD。

React-DnD 不仅限于简单的列表排序,它还支持更复杂的拖拽场景,如多个放置目标、自定义拖拽预览、嵌套拖拽等。希望这篇教程能帮助你开始在 React 应用中实现出色的拖拽交互!

滚动至顶部