跟着官方示例学习 @tanStack-table --- Row Dnd

🌲系列一:跟着官方示例学习 @tanStack-table --- Basic

🌲系列二:跟着官方示例学习 @tanStack-table --- Header Groups

🌲系列三:跟着官方示例学习 @tanStack-table --- Column Filters

🌲系列四:跟着官方示例学习 @tanStack-table --- Column Ordering

🌲系列五:跟着官方示例学习 @tanStack-table --- Sticky Column Pinning

🌲系列六:跟着官方示例学习 @tanStack-table --- Column Sizing

🌲系列七:跟着官方示例学习 @tanStack-table --- Expanding

🌲系列八:跟着官方示例学习 @tanStack-table --- Pagination


还记得在之前的文章中,我们已经实现了列排序 跟着官方示例学习 @tanStack-table --- Column Ordering,今天我们就来实现让用户可以随心所欲地拖动表格的每一行,就像排乐高积木一样!

我们会用到 @dnd-kit ------ 一个强大又灵活的 React 拖拽库,和 TanStack Table 配合起来简直是天作之合!

🎛️ 为什么需要自定义 rowId

TanStack Table 默认使用数组索引作为 row.id。这对一般场景没问题,但在拖拽排序中,索引会变化,导致状态错乱。

我们必须提供一个稳定的 ID,比如:

tsx 复制代码
useReactTable({
  data,
  columns,
  getRowId: (row) => row.userId,
});

✅ 这是整个拖拽功能的基石。如果你忽略它,调试时就会遇到:"为什么我拖了,但数据没变?" 的迷之场景。

✨ 拖拽的三要素:传感器、排序策略和回调处理

在正式开始前,我们来看看我们需要哪些基础结构:

tsx 复制代码
const sensors = useSensors(
  useSensor(MouseSensor, {}),
  useSensor(TouchSensor, {}),
  useSensor(KeyboardSensor, {})
);

这是 dnd-kit 的"感知器"们,负责检测你是在鼠标拖、手指划、还是键盘控制。

🧲 拖拽把手 Cell

我们需要一个专属的拖动按钮------小小一枚 🟰 图标,它其实是个按钮,绑定了拖拽事件。

tsx 复制代码
const RowDragHandleCell = ({ rowId }: { rowId: string }) => {
  const { attributes, listeners } = useSortable({ id: rowId });
  return <button {...attributes} {...listeners}>🟰</button>;
};

📌 这个组件会被插入到表格的第一列,用来当"拖动的起点"。

⌚️ 可拖拽的表格行 DraggableRow

每一行都要支持拖动,这时候我们就得使用 useSortable<tr> 包一层魔法:

tsx 复制代码
const DraggableRow = ({ row }: { row: Row<Person> }) => {
  const { transform, transition, setNodeRef, isDragging } = useSortable({
    id: row.original.userId,
  });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.8 : 1,
    zIndex: isDragging ? 1 : 0,
    position: "relative",
  };

  return (
    <tr ref={setNodeRef} style={style}>
      {row.getVisibleCells().map((cell) => (
        <td key={cell.id} style={{ width: cell.column.getSize() }}>
          {flexRender(cell.column.columnDef.cell, cell.getContext())}
        </td>
      ))}
    </tr>
  );
};

当你开始拖拽时,isDragging 会变成 true,我们可以让这行有些"飘起来"的视觉效果,视觉上的"提起感"来自于 opacity + zIndex + boxShadow

🔗 SortableContext 是桥梁

我们用 SortableContext 包住了所有 <tr>

tsx 复制代码
<SortableContext items={dataIds} strategy={verticalListSortingStrategy}>
  {table.getRowModel().rows.map((row) => (
    <DraggableRow key={row.id} row={row} />
  ))}
</SortableContext>

这是 dnd-kit 的核心设计:告诉引擎,谁可以被排序、按什么方式排序。

  • items: 是 rowID 列表,它必须与你的 data 顺序一致

  • strategy: 排序策略。常用的有:

    • verticalListSortingStrategy:垂直列表

    • rectSortingStrategy:网格布局

🧠 处理拖拽结束后的排序逻辑

拖动行的目的当然是为了重新排序,所以我们需要在 onDragEnd 中做个数据重排:

tsx 复制代码
function handleDragEnd(event: DragEndEvent) {
  const { active, over } = event;
  if (active && over && active.id !== over.id) {
    setData((data) => {
      const oldIndex = dataIds.indexOf(active.id);
      const newIndex = dataIds.indexOf(over.id);
      return arrayMove(data, oldIndex, newIndex);
    });
  }
}

🏗️ 整体结构 App

别忘了,SortableContext 需要包裹可拖动的元素区域,items 要传入对应的 id 列表,用于排序。

我们的表格组件最后就像这样:

tsx 复制代码
<DndContext
  collisionDetection={closestCenter}
  modifiers={[restrictToVerticalAxis]}
  onDragEnd={handleDragEnd}
  sensors={sensors}
>
  <table>
    <thead>{/* 渲染头部 */}</thead>
    <tbody>
      <SortableContext items={dataIds} strategy={verticalListSortingStrategy}>
        {table.getRowModel().rows.map((row) => (
          <DraggableRow key={row.id} row={row} />
        ))}
      </SortableContext>
    </tbody>
  </table>
</DndContext>

🔔 对官方示例代码可能存在一些删减的情况

代码地址🔗:Gitee

官方代码地址🔗: @tanStack/react-table

相关推荐
小满zs3 小时前
Zustand 第五章(订阅)
前端·react.js
涵信4 小时前
第一节 基础核心概念-TypeScript与JavaScript的核心区别
前端·javascript·typescript
谢尔登4 小时前
【React】常用的状态管理库比对
前端·spring·react.js
编程乐学(Arfan开发工程师)4 小时前
56、原生组件注入-原生注解与Spring方式注入
java·前端·后端·spring·tensorflow·bug·lua
小公主5 小时前
JavaScript 柯里化完全指南:闭包 + 手写 curry,一步步拆解原理
前端·javascript
姑苏洛言6 小时前
如何解决答题小程序大小超过2M的问题
前端
一嘴一个橘子7 小时前
获取DOM
react.js
GISer_Jing7 小时前
JWT授权token前端存储策略
前端·javascript·面试
开开心心就好7 小时前
电脑扩展屏幕工具
java·开发语言·前端·电脑·php·excel·batch
拉不动的猪7 小时前
es6常见数组、对象中的整合与拆解
前端·javascript·面试