手把手教你用 React 实现可拖拽排序的 Ant Design 表格

手把手教你用 React 实现可拖拽排序的 Ant Design 表格

在这里插入图片描述

一、前置知识准备

1.1 什么是高阶组件(HOC)?

高阶组件就像"组件包装纸",可以为现有组件添加额外功能。例如:

const EnhancedComponent = withHOC(BaseComponent);
  • react-sortable-hoc 的核心就是通过三个 HOC 实现拖拽功能

1.2 不可变数据(Immutable Data)

在 React 中,直接修改状态可能导致渲染问题。正确做法:

// 错误 ❌
const newArr = arr.splice(index, 1);

// 正确 ✅ 
const newArr = [...arr.slice(0, index), ...arr.slice(index+1)];

这也是使用 array-move 库的原因。

1.3 拖拽排序基本原理

  1. 监听鼠标/触摸事件
  2. 计算元素位置变化
  3. 更新数据顺序
  4. 重新渲染界面

二、核心实现详解

2.1 安装依赖

npm install react-sortable-hoc array-move

2.2 创建拖拽手柄组件

const DragHandle = SortableHandle(() => (
  <MenuOutlined style={{ 
    cursor: "grab",
    padding: "0 8px",
    opacity: 0.5 
  }} />
));

2.3 包裹表格组件

// 可拖拽的表格行
const SortableItem = SortableElement(
  (props: React.HTMLAttributes<HTMLTableRowElement>) => <tr {...props} />
);

// 可拖拽的表格体
const SortableBody = SortableContainer(
  (props: React.HTMLAttributes<HTMLTableSectionElement>) => <tbody {...props} />
);

2.4 状态管理与排序逻辑

const [dataSource, setDataSource] = useState(initialData);

const onSortEnd = ({ oldIndex, newIndex }: SortEnd) => {
  if (oldIndex === newIndex) return;
  
  const newData = arrayMoveImmutable(
    dataSource, 
    oldIndex,
    newIndex
  );
  
  setDataSource(newData);
};

2.5 完整组件集成

const DraggableTable = () => {
  return (
    <Table
      components={{
        body: {
          wrapper: (props) => (
            <SortableBody
              useDragHandle
              helperClass="dragging-row"
              onSortEnd={onSortEnd}
              {...props}
            />
          ),
          row: ({ rowKey, ...rest }) => {
            const index = dataSource.findIndex(
              (item) => item.key === rowKey
            );
            return <SortableItem index={index} {...rest} />;
          }
        }
      }}
      rowKey="key"
      columns={[
        {
          title: '拖拽',
          render: () => <DragHandle />
        },
        // 其他列...
      ]}
      dataSource={dataSource}
    />
  );
};

三、关键问题解析

3.1 为什么需要 data-row-key?

Ant Design Table 会自动为每行注入 data-row-key 属性,其值等于 rowKey 属性配置的字段值。我们通过这个属性找到对应的数据索引。

3.2 拖拽动画实现原理

通过 helperClass 添加 CSS 动画:

.dragging-row {
  background: #fafafa;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  transform: rotate(2deg);
  transition: transform 0.2s;
}

3.3 性能优化技巧

虚拟滚动‌:集成 react-virtualized
import { Table } from 'react-virtualized';

节流处理‌:对 onSortEnd 添加节流
import { throttle } from 'lodash';
const throttledSort = throttle(onSortEnd, 300);

四、横向对比:主流拖拽库选型

库名优点缺点适用场景
react-sortable-hoc轻量简单,与 AntD 集成方便功能较基础简单表格/列表
react-beautiful-dnd动画流畅,支持嵌套拖拽学习曲线陡峭看板类复杂应用
dnd-kit现代化 API,TypeScript 友好生态不够成熟需要高度定制的项目
SortableJS不依赖 React,功能强大需要手动集成 React多框架混合项目

五、扩展功能实现

5.1 跨表格拖拽

// 使用 react-dnd 实现
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

const App = () => (
  <DndProvider backend={HTML5Backend}>
    <Table1 />
    <Table2 />
  </DndProvider>
);

5.2 拖拽保存状态

// 每次排序后调用 API
const onSortEnd = async ({ oldIndex, newIndex }) => {
  const newData = arrayMoveImmutable(...);
  setDataSource(newData);
  
  try {
    await axios.post('/api/save-order', {
      order: newData.map(item => item.id)
    });
  } catch (err) {
    // 回滚操作
    setDataSource(prevData);
  }
};

六、最佳实践建议

  1. 唯一标识‌:确保 rowKey 始终唯一且稳定
  2. 错误边界‌:添加拖拽失败的恢复机制
  3. 移动端适配‌:
import { TouchBackend } from 'react-dnd-touch-backend';
<DndProvider backend={TouchBackend} options={{ enableMouseEvents: true }}>

  1. 无障碍支持‌:
<DragHandle
 aria-label="拖拽手柄"
 tabIndex={0}
/>

七、总结

通过本文,您已掌握:
✅ React 高阶组件原理
✅ 不可变数据操作
✅ 拖拽排序核心实现
✅ 性能优化技巧
✅ 主流拖拽库选型指南

在实际项目中,请根据具体需求选择合适方案。对于简单表格推荐使用 react-sortable-hoc + array-move 的组合,复杂场景可考虑 react-beautiful-dnd


附录‌:常见问题速查表

现象可能原因解决方案
拖拽后数据错位rowKey 配置错误检查数据唯一标识字段
拖拽时页面卡顿大数据量未优化添加虚拟滚动
移动端无法拖拽未启用触摸事件支持集成 react-dnd-touch-backend
拖拽样式异常CSS 类名冲突添加样式作用域隔离
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漠月瑾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值