新闻详情

新闻详情

首页 / 资讯中心 / 详情

《Ant Design Table 拖拽排序失效?99% 的人都忽略了这个小细节!》

发布时间:2026/11/3 8:32:50来源:尧图网络
《Ant Design Table 拖拽排序失效?99% 的人都忽略了这个小细节!》
一、现象描述“我参照 Ant Design 官方示例实现了表格行拖拽排序。但拖拽时发现顺序乱跳甚至完全不生效而且没有任何报错。”为什么明明代码和官方示例一样却不行二、排查过程第一反应以为是DraggableBodyRow写错了检查useDrag/useDrop是否配对检查onRow是否正确传递了index和moveRow检查components是否注册发现关键线索年龄列有默认排序{ title: 年龄, sorter: (a, b) a.age - b.age, defaultSortOrder: descend, // 就是它 }推测拖拽修改了dataSource但排序逻辑立即介入把数据重新排列覆盖了拖拽结果验证注释掉defaultSortOrder拖拽恢复正常 ✅三、原理分析Ant Design Table 的排序机制排序状态下表格显示的是排序后的数据而不是dataSource的原始顺序拖拽修改的是dataSource但排序逻辑比拖拽逻辑后执行覆盖了拖拽的结果本质上是两种“排序”逻辑的冲突一种是用户点击列头触发的排序一种是拖拽触发的排序四、解决方案方案做法适用场景方案一移除defaultSortOrder让用户手动点击排序优先保证拖拽功能方案二拖拽开始时清除排序状态两者共存但拖拽时禁用列排序方案三在拖拽结束后重新应用排序保留排序状态拖拽后按当前排序重新排列五、总结与建议教训在使用 Ant Design 高级功能时各个配置项之间可能存在冲突排查时要全面建议如果拖拽和排序都要支持优先考虑用户体验——拖拽时自动清除排序状态或者用弹窗提示“排序状态下无法拖拽”Debug 方法论遇到问题先最小化复现移除其他配置再逐步添加import update from immutability-helper; import React, { useCallback, useRef, useState } from react; import { DndProvider, useDrag, useDrop } from react-dnd; import { HTML5Backend } from react-dnd-html5-backend; import { Button, Form, Input, Table } from antd; import { LockOutlined, UserOutlined } from ant-design/icons; import ./index.scope.css const type DraggableBodyRow; const DraggableBodyRow ({ index, moveRow, className, style, ...restProps }) { const ref useRef(null); const [{ isOver, dropClassName }, drop] useDrop({ accept: type, collect: (monitor) { const { index: dragIndex } monitor.getItem() || {} if (dragIndex index) { return {} } return { isOver: monitor.isOver(), dropClassName: dragIndex index ? drop-over-downward : drop-over-upward } }, drop: (item) { moveRow(item.index, index) } }) const [, drag] useDrag({ type, item: { index, }, collect: (monitor) ({ isDragging: monitor.isDragging(), }), }) drop(drag(ref)) return ( tr ref{ref} className{${className}${isOver ? dropClassName : }} style{{ cursor: move, ...style }} {...restProps} {restProps.children} /tr ) } const DragTable () { const [form] Form.useForm(); const [selectedRowKeys, setSelectedRowKeys] useState([]); const [sortOrder, setSortOrder] useState(null); const getSortTooltip () { if (sortOrder ascend) return 点击降序; if (sortOrder descend) return 取消排序; return 点击升序; }; const [dataSource, setDataSource] useState([ { key: 1, name: 胡胡, age: 32, address: 西湖区湖底公园1号, hobby: 唱歌,跳舞,弹钢琴,画画,写诗,写小说,写剧本,写歌词,写散文,写随笔,写日记,写博客,写微博,写朋友圈,写抖音,写快手,写B站,写知乎,写豆瓣,写小红书,写公众号,写头条号,写百家号,写企鹅号,写新浪微博,写网易号,写搜狐号,写凤凰号,写今日头条号, job: 歌手, salary: 100万, isMarried: 否, hometown: 四川, certification: 无, isRenter: 否, familySize: 3, retirementDate: 2050-01-01, children: [ { key: 1-1, name: 喵喵, age: 42, address: 西湖区湖底公园1号, } ] }, { key: 2, name: 胡彦祖, age: 42, address: 西湖区湖底公园1号, }, { key: 3, name: 胡巴, age: 32, address: 西湖区湖底公园1号, hobby: 唱歌,跳舞,弹钢琴,画画,写诗,写小说,写剧本,写歌词,写散文,写随笔,写日记,写博客,写微博,写朋友圈,写抖音,写快手,写B站,写知乎,写豆瓣,写小红书,写公众号,写头条号,写百家号,写企鹅号,写新浪微博,写网易号,写搜狐号,写凤凰号,写今日头条号, job: 歌手, salary: 100万, isMarried: 否, hometown: 四川, certification: 无, isRenter: 否, familySize: 3, retirementDate: 2050-01-01, }, ]) const defaultColumns [ { title: 姓名, dataIndex: name, key: name, width: 150, filters: [ { text: 胡胡, value: 胡胡, children: [ { text: 胡胡子 }] }, { text: 胡彦祖, value: 胡彦祖, }, ], filterMode: tree, filterSearch: true, onFilter: (value, record) record.name.indexOf(value) 0, }, { title: 年龄, dataIndex: age, key: age, width: 100, sorter: (a, b) a.age - b.age, defaultSortOrder: descend, showSorterTooltip: { title: getSortTooltip() }, editable: true, }, { title: 住址, dataIndex: address, key: address, width: 100, sorter: (a, b) a.name.length - b.name.length, sortDirections: [descend], showSorterTooltip: { title: getSortTooltip() }, }, { title: 爱好, dataIndex: hobby, key: hobby, width: 300, editable: true, }, { title: 工作, dataIndex: job, key: job, width: 150, editable: true, }, { title: 薪资, dataIndex: salary, key: salary, }, { title: 是否成家-老家, dataIndex: isMarried, key: isMarried, width: 200, }, { title: 老家, dataIndex: hometown, key: hometown, width: 100, }, { title: 技能证书, dataIndex: certification, key: certification, width: 150, }, { title: 是否租房, dataIndex: isRenter, key: isRenter, width: 100, }, { title: 几口人, dataIndex: familySize, key: familySize, width: 100, }, { title: 退休时间, dataIndex: retirementDate, key: retirementDate, width: 150, fixed: right, }, ]; const components { body: { row: DraggableBodyRow, }, }; const moveRow useCallback( (dragIndex, hoverIndex) { const dragRow dataSource[dragIndex]; setDataSource( update(dataSource, { $splice: [ [dragIndex, 1], [hoverIndex, 0, dragRow], ], }), ); }, [dataSource], ); const onFinish (values) { console.log(Received values of form: , values); setSelectedRowKeys([]) }; const handleTableChange (pagination, filters, sorter) { console.log(Table change:, { pagination, filters, sorter }); if (sorter.field) { setSortOrder(sorter.order); } }; return div Form form{form} namehorizontal_login layoutinline onFinish{onFinish} Form.Item nameusername rules{[{ required: true, message: Please input your username! }]} Input prefix{UserOutlined classNamesite-form-item-icon /} placeholderUsername / /Form.Item Form.Item namepassword rules{[{ required: true, message: Please input your password! }]} Input prefix{LockOutlined classNamesite-form-item-icon /} typepassword placeholderPassword / /Form.Item Form.Item shouldUpdate {() ( Button typeprimary htmlTypesubmit disabled{ !form.isFieldsTouched(true) || !!form.getFieldsError().filter(({ errors }) errors.length).length } 查询 /Button )} /Form.Item /Form div classNametable-container DndProvider backend{HTML5Backend} Table key{dataSource.map(item item.key).join(,)} components{components} bordered dataSource{dataSource} columns{defaultColumns} onChange{handleTableChange} onRow{(_, index) { const attr { index, moveRow } return attr }} scroll{{ x: max-content }} rowSelection{{ type: checkbox, selectedRowKeys: selectedRowKeys, onChange: (selectedRowKeys, selectedRows) { setSelectedRowKeys(selectedRowKeys); console.log(selectedRowKeys: ${selectedRowKeys}, selectedRows: , selectedRows); }, }} /; /DndProvider /div /div } export default DragTable
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/3 7:07:39

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/2 5:53:30

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/2 5:53:30

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/2 5:53:28

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/2 5:53:27

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/2 5:53:27

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉