跟着官方示例学习 @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

相关推荐
li35744 小时前
将已有 Vue 项目通过 Electron 打包为桌面客户端的完整步骤
前端·vue.js·electron
Icoolkj4 小时前
VuePress 与 VitePress 深度对比:特性、差异与选型指南
前端·javascript·vue.js
excel4 小时前
CNN 分层详解:卷积、池化到全连接的作用与原理
前端
excel4 小时前
CNN 多层设计详解:从边缘到高级特征的逐层学习
前端
西陵6 小时前
Nx带来极致的前端开发体验——任务编排
前端·javascript·架构
大前端helloworld6 小时前
从初中级如何迈入中高级-其实技术只是“入门卷”
前端·面试
东风西巷7 小时前
Balabolka:免费高效的文字转语音软件
前端·人工智能·学习·语音识别·软件需求
萌萌哒草头将军7 小时前
10个 ES2025 新特性速览!🚀🚀🚀
前端·javascript·vue.js
半夏陌离7 小时前
SQL 入门指南:排序与分页查询(ORDER BY 多字段排序、LIMIT 分页实战)
java·前端·数据库