React-DnD logo

React-DnD

React 拖拽库的"元老级"方案,以极致的灵活性和后端抽象著称,适合高度自定义的复杂拖拽场景。

拖拽排序React拖拽库开源

React 生态早期的拖拽库,采用 Flux 数据模式设计,通过 Drag Source、Drop Target 等原语实现高度自定义的复杂拖拽场景。

React-DnD

React 拖拽库的"元老级"方案,以极致的灵活性和后端抽象著称,适合高度自定义的复杂拖拽场景。

技术简介说明

React-DnD 是 React 生态中最早期的拖拽库之一,由 Redux 作者 Dan Abramov 于 2015 年创建,后由 Jordan Gensler 和 Chris Trevino 维护。它采用独特的 Flux 数据模式设计,将拖拽状态管理独立于 React 组件之外,通过 actions 和 reducers 管理拖拽生命周期。

React-DnD 的核心设计哲学是极致的灵活性——它不预设任何拖拽模式(列表、网格、看板),而是提供最底层的原语:Drag Source(拖拽源)、Drop Target(放置目标)、Drag Layer(拖拽层)和 Backend(后端抽象)。开发者通过这些原语自由组合出任何拖拽场景,从简单的排序列表到复杂的棋盘游戏。

然而,这种灵活性也带来了陡峭的学习曲线冗长的 API。在 2025-2026 年,React-DnD 已经4 年未发布新版本(v16.0.1 发布于 2022 年 4 月),且未官方支持 React 19。社区正逐步转向 dnd-kit 和 Atlassian 的 Pragmatic drag-and-drop。

基本信息

快速上手

安装

# 安装核心库和 HTML5 后端
npm install react-dnd react-dnd-html5-backend
 
# 如需触摸设备支持
npm install react-dnd-touch-backend
 
# 如需测试支持
npm install react-dnd-test-backend
 
# 多后端自动切换(桌面 + 移动端)
npm install react-dnd-multi-backend

基础配置

import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'
 
function App() {
  return (
    <DndProvider backend={HTML5Backend}>
      {/* 所有拖拽组件必须在此 Provider 内 */}
      <Board />
    </DndProvider>
  )
}

最小示例

import { useDrag, useDrop, DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'
 
const ItemTypes = {
  CARD: 'card',
}
 
// 可拖拽的卡片组件
function Card({ id, text, index, moveCard }) {
  const [{ isDragging }, dragRef] = useDrag({
    type: ItemTypes.CARD,
    item: { id, index },
    collect: (monitor) => ({
      isDragging: monitor.isDragging(),
    }),
  })
 
  const [, dropRef] = useDrop({
    accept: ItemTypes.CARD,
    hover: (draggedItem) => {
      if (draggedItem.index !== index) {
        moveCard(draggedItem.index, index)
        draggedItem.index = index
      }
    },
  })
 
  return (
    <div
      ref={(node) => { dragRef(node); dropRef(node) }}
      style={{ opacity: isDragging ? 0.5 : 1 }}
    >
      {text}
    </div>
  )
}
 
// 主组件
export function SortableCards() {
  const [cards, setCards] = useState([
    { id: 1, text: 'Card 1' },
    { id: 2, text: 'Card 2' },
    { id: 3, text: 'Card 3' },
  ])
 
  const moveCard = (fromIndex, toIndex) => {
    setCards((prev) => {
      const newCards = [...prev]
      const [removed] = newCards.splice(fromIndex, 1)
      newCards.splice(toIndex, 0, removed)
      return newCards
    })
  }
 
  return (
    <DndProvider backend={HTML5Backend}>
      {cards.map((card, index) => (
        <Card
          key={card.id}
          id={card.id}
          text={card.text}
          index={index}
          moveCard={moveCard}
        />
      ))}
    </DndProvider>
  )
}

核心概念与架构

架构设计

React-DnD 基于 Flux 数据模式设计,将拖拽状态管理独立于 React:

┌───────────────────────────────────────┐
│             DndProvider               │
│  (提供拖拽上下文,绑定 Backend)         │
├───────────────────────────────────────┤
│                                       │
│  ┌─────────────┐  ┌────────────────┐ │
│  │ DragSource  │  │  DropTarget    │ │
│  │ (拖拽源)    │  │  (放置目标)    │ │
│  │ useDrag()   │  │  useDrop()     │ │
│  └──────┬──────┘  └──────┬─────────┘ │
│         │                │           │
│  ┌──────▼────────────────▼─────────┐ │
│  │         DragDropManager         │ │
│  │    (独立于 React 的状态管理)     │ │
│  │    actions + reducers 模式      │ │
│  └──────────────┬──────────────────┘ │
│                 │                    │
│  ┌──────────────▼──────────────────┐ │
│  │          Backend                │ │
│  │  (HTML5 | Touch | Test)        │ │
│  └─────────────────────────────────┘ │
│                                       │
│  ┌─────────────────────────────────┐ │
│  │         DragLayer               │ │
│  │    (自定义拖拽预览层)            │ │
│  │    useDragLayer()               │ │
│  └─────────────────────────────────┘ │
└───────────────────────────────────────┘

核心 Hooks API

Hook功能返回值
useDrag使组件成为可拖拽源[collectedProps, dragRef, dragPreviewRef]
useDrop使组件成为放置目标[collectedProps, dropRef]
useDragLayer监听拖拽状态,用于自定义预览层collectedProps

Backend 系统

Backend 是 React-DnD 最独特的设计——它将底层输入检测抽象为可替换的后端:

Backend功能说明
HTML5Backend默认桌面后端使用 HTML5 Drag API,支持跨应用拖拽
TouchBackend触摸设备后端支持移动端触摸拖拽
TestBackend测试后端可编程模拟拖拽交互,无需 DOM
MultiBackend多后端切换自动检测输入类型并切换(社区包)

核心数据模型

概念说明
Items纯 JavaScript 对象,表示拖拽的数据(&#123; id, index, type &#125;
Types字符串类型,用于匹配拖拽源和放置目标的兼容性
Monitors监控拖拽状态变化,提供 isDragging()canDrop() 等方法
Collect 函数从 Monitor 中提取状态并注入组件 props

事件生命周期

拖拽开始 (beginDrag)
    ↓
拖拽中 (hover) ← 高频触发
    ↓
放置 (drop)
    ↓
拖拽结束 (endDrag)

核心特性

  • 极致灵活性:最底层的拖拽原语,可构建任何拖拽场景(棋盘、卡片游戏、看板、文件管理器)
  • Backend 系统:将输入检测抽象为可替换后端,支持 HTML5、触摸、测试等多种后端
  • DragLayer 自定义预览:完全控制拖拽预览效果,可渲染任意 React 组件作为预览
  • TestBackend:无需 DOM 即可测试拖拽逻辑,单元测试友好
  • TypeScript 原生支持:完整的类型定义
  • Flux 数据模式:拖拽状态独立于 React,支持复杂的状态流转
  • 经过实战检验:大规模采用,npm 周下载量 ~450 万,大量生产项目使用
  • 多后端支持:通过 react-dnd-multi-backend 自动切换桌面/移动端
  • 细粒度控制:完全控制拖拽生命周期每个阶段

生态图

                    ┌──────────────────┐
                    │    react-dnd     │  ← 核心库
                    │    v16.0.1       │
                    └────────┬─────────┘
                             │
            ┌────────────────┼────────────────┐
            │                │                │
   ┌────────▼──────┐ ┌──────▼───────┐ ┌──────▼────────┐
   │ HTML5 Backend │ │Touch Backend │ │ Test Backend  │
   │ (桌面拖拽)    │ │ (移动端)     │ │ (单元测试)    │
   └───────────────┘ └──────────────┘ └───────────────┘

   社区生态:
   ┌──────────────────────────────────────────────┐
   │ • react-dnd-multi-backend (多后端自动切换)    │
   │ • react-dnd-preview (自定义拖拽预览)          │
   │ • react-dnd-scroll-mouse (滚动支持)          │
   │ • 大量教程和社区示例                          │
   └──────────────────────────────────────────────┘

   已废弃的相关库:
   ┌──────────────────────────────────────────────┐
   │ • react-beautiful-dnd ❌ (Atlassian 弃用)    │
   │ • react-sortable-hoc ❌ (停止维护)            │
   └──────────────────────────────────────────────┘

关键依赖

  • react-dnd-html5-backend:默认后端,使用 HTML5 Drag API
  • react-dnd-touch-backend:触摸设备后端(~100 万周下载)
  • react-dnd-test-backend:测试后端(无 DOM 依赖)
  • react:需要 React ≥ 16.8(Hooks 支持)

适用场景

  • 棋盘类游戏:官方经典示例,棋子在网格间拖拽,利用 Type 系统控制兼容性
  • 看板系统(Kanban):任务卡片在列间拖拽,利用 useDrag + useDrop 组合
  • 卡片游戏:复杂的多对多拖拽关系,需要精细控制拖拽行为
  • 自定义拖拽预览:需要完全控制预览效果,使用 useDragLayer 渲染自定义组件
  • 多上下文拖拽:不同区域接受不同类型的拖拽项(利用 Type 匹配系统)
  • 需要测试拖拽逻辑:TestBackend 支持无 DOM 单元测试
  • 需要桌面文件拖入:HTML5Backend 支持从桌面拖入文件(区别于 dnd-kit)

开发与工程化

开发流程

# 克隆仓库
git clone https://github.com/react-dnd/react-dnd.git
cd react-dnd
 
# 安装依赖
yarn install
 
# 启动开发文档站
yarn start
 
# 构建
yarn build
 
# 测试
yarn test

项目结构

react-dnd/
├── packages/
│   ├── core/                    # react-dnd 核心
│   │   └── src/
│   │       ├── hooks/           # useDrag, useDrop, useDragLayer
│   │       ├── common/          # 类型定义、DndProvider
│   │       └── internals/       # DragDropManager
│   ├── backend-html5/           # HTML5 后端
│   ├── backend-touch/           # 触摸后端
│   ├── backend-test/            # 测试后端
│   └── test-utils/              # 测试工具
├── examples/                    # 示例项目
│   ├── chessboard/             # 棋盘
│   ├── kanban/                 # 看板
│   └── card-game/              # 卡片游戏
└── docs/                        # 文档

性能与安全

性能特点

指标表现说明
Bundle 大小~23KB (gzipped)大于 dnd-kit (~6KB)
100 项流畅无明显性能问题
500 项可接受部分掉帧
1000+ 项~45fps性能明显下降
渲染方式位置更新不如 CSS transform + GPU 加速
Tree-shakeable部分不如 dnd-kit 完全支持

性能优化建议

  1. 使用 useDragLayer 自定义预览:避免拖拽时重渲染整个组件树

    function CustomDragLayer() {
      const { item, offset } = useDragLayer((monitor) => ({
        item: monitor.getItem(),
        offset: monitor.getClientOffset(),
      }))
      // 渲染自定义预览
    }
  2. 避免 collect 函数过度调用:collect 函数在每次状态变化时都会执行,保持轻量

  3. 使用 React.memo 优化:减少不必要的子组件重渲染

  4. 大数据集配合虚拟化:1000+ 项时性能下降,需配合虚拟滚动库

安全注意事项

  • React-DnD 不涉及用户输入,无 XSS 风险
  • SSR 注意:Issue #1721 报告 SSR 中存在内存泄漏,需正确处理 DndProvider 生命周期
  • 拖拽 Item 数据避免包含敏感信息

已知性能瓶颈

  • 1000+ DropTarget:所有 DropTarget 的 collect 函数都会调用,影响性能(Issue #421)
  • 高频 hover 事件:拖拽过程中 hover 高频触发,如在 hover 中执行 state 更新可能性能问题
  • CSS transform 未优化:不如 dnd-kit 的 GPU 加速方案

技术对比

与 React 拖拽库对比

维度React-DnDdnd-kitreact-beautiful-dndPragmatic DnD
维护状态⚠️ 4 年无更新✅ 活跃❌ 已弃用✅ Atlassian
包体积~23KB~6KB~30KB~3.5KB
学习曲线陡峭中等简单中等
上手时间4-6 小时3-4 小时2-3 小时3-4 小时
灵活性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
无障碍⚠️ 需手动✅ 内建✅ 键盘⚠️ 需手动
触摸支持⚠️ 需 TouchBackend✅ 原生⚠️ 有限⚠️ 需配置
HTML5 DnD✅ 使用❌ 不使用❌ 不使用✅ 使用
外部文件拖入✅ 支持❌ 不支持❌ 不支持✅ 支持
虚拟化⚠️ 需自行实现✅ 原生⚠️ 差✅ 原生
React 19 支持❓ 未验证✅ 支持❌ 不支持✅ 支持

选型建议

场景推荐
React 项目通用拖拽dnd-kit(默认选择)
需要桌面文件拖入React-DnDPragmatic DnD
复杂自定义拖拽(棋盘、卡片游戏)React-DnD(灵活性最强)
遗留 React-DnD 项目短期继续使用,中期评估迁移到 dnd-kit
新项目不推荐 React-DnD(维护停滞)

最佳实践

生产环境最佳实践

  1. 始终使用 Hooks API:装饰器 API(@DragSource@DropTarget)已过时

    // ✅ 推荐:Hooks API
    const [{ isDragging }, dragRef] = useDrag({ ... })
     
    // ❌ 避免:装饰器 API(已过时)
    @DragSource('card', spec, connect)
    class Card extends React.Component { }
  2. 正确放置 DndProvider:必须在所有拖拽组件的外部

    // ✅ 正确
    <DndProvider backend={HTML5Backend}>
      <App />
    </DndProvider>
     
    // ❌ 错误:不能在 App 内部
    function App() {
      return <DndProvider backend={HTML5Backend}>...</DndProvider>
    }
  3. 使用 TestBackend 做测试:避免依赖 DOM

    import { TestBackend } from 'react-dnd-test-backend'
     
    render(
      <DndProvider backend={TestBackend}>
        <MyComponent />
      </DndProvider>
    )
  4. 多后端场景用 react-dnd-multi-backend:自动切换桌面/移动端

  5. 使用 DragLayer 自定义预览:减少不必要的重渲染

  6. 合理定义 Item Type:Type 是字符串常量,建议使用常量对象集中管理

    const ItemTypes = { CARD: 'card', COLUMN: 'column' } as const

常见陷阱

陷阱说明解决方案
忘记 DndProvider导致 "Expected drag drop context" 错误确保 DndProvider 在组件树顶层
SSR 内存泄漏Issue #1721 报告 SSR 内存泄漏正确处理 DndProvider 生命周期
React 19 兼容性未官方支持(Issue #3655)监控兼容性,或迁移到 dnd-kit
collect 函数过度使用所有 DropTarget 都调用 collect保持 collect 函数轻量
hover 中频繁 setState高频 hover 事件导致性能问题使用 React.memo 或节流
Item Type 不匹配Drag Source 和 Drop Target 的 type 不一致使用常量集中管理 Type
移动端无触摸支持忘记安装 TouchBackend使用 react-dnd-multi-backend

技术局限与边界

已知限制

限制说明
维护停滞4 年无新版本(v16.0.1,2022 年 4 月)
React 19 未知Issue #3655 仍开放,无官方回应
学习曲线陡峭需要理解 Monitors、Backends、Item/Type 系统
API 冗长相比 dnd-kit 更复杂,样板代码多
无障碍需手动实现无内建 ARIA 支持、键盘导航、屏幕阅读器通知
触摸支持需额外 Backend不像 dnd-kit 原生支持
Bundle 较大~23KB vs dnd-kit ~6KB
性能瓶颈1000+ 项时性能下降至 ~45fps
内置排序能力有限需要额外实现排序逻辑

不适用场景

  • 简单排序列表 → 推荐 dnd-kit(开箱即用)
  • 无障碍优先项目 → 推荐 dnd-kit(内建 WCAG 2.1 AA)
  • 新项目(2026 年) → 推荐 dnd-kit 或 Pragmatic DnD
  • 大规模数据(>1000 项) → 推荐 dnd-kit(性能更好)

迁移注意事项

从 React-DnD 迁移到 dnd-kit:

  • 预计 1-2 天(中等复杂度项目)
  • 主要差异:React-DnD 使用 HTML5 DnD API,dnd-kit 使用自定义指针事件
  • dnd-kit 不支持从桌面拖入文件,如需此功能考虑 Pragmatic DnD

学习资源

2026 年现状

维护状态评估

指标状态
最新版本v16.0.1(2022 年 4 月)
距上次发布~4 年
npm 周下载量~450 万(大量存量项目)
GitHub Stars21,600+
Open Issues444
React 19 支持❓ 未验证,Issue #3655 无回应
维护活跃度⚠️ 事实上已停滞

社区态度

正面:

  • 仍被用于复杂拖拽场景(棋盘、卡片游戏)
  • react-beautiful-dnd 废弃后,部分用户迁移到 React-DnD
  • 444 个 open issues 显示仍有活跃用户基础
  • 450 万周下载量说明大量存量项目

负面:

  • 2026 年多篇文章不再推荐 React-DnD 作为首选
  • 社区转向 dnd-kit 或 Pragmatic drag-and-drop
  • React 19 兼容性问题未解决引发担忧
  • 4 年无 npm 发布,无安全补丁或新功能

发展趋势

  • 事实上已停滞:虽有 GitHub 活动,但无实际发布
  • 被新库替代:dnd-kit 成为 React 拖拽的默认选择
  • 存量项目仍可靠:450 万周下载量,短期内不会消失
  • 新项目不推荐:除非确实需要 HTML5 DnD API 的极端灵活性

选型结论

React-DnD 在 2026 年事实上已停滞维护(4 年无更新,React 19 未验证)。 新项目应选择 dnd-kit 或 Pragmatic drag-and-drop。遗留项目短期可继续使用,中期建议评估迁移到 dnd-kit。唯一仍推荐 React-DnD 的场景是需要 HTML5 DnD API(桌面文件拖入)且需要极致灵活性的复杂自定义拖拽。