React表格实现列拖拽功能draggedFromIndex异常问题求助
问题排查及修复方案
核心问题1:缺少data-position属性绑定
你在代码中通过e.currentTarget.dataset.position获取列索引,但<th>元素没有绑定该属性,导致draggedFromIndex、draggedToIndex都是空值。
核心问题2:状态更新未使用函数式写法,存在异步闭包问题
React的setState是异步更新的,你直接基于当前dragInfos展开更新,多次连续触发状态更新时会拿到旧的状态值,导致拖拽信息记录错误。
核心问题3:dragOver事件未阻止默认行为
原生拖拽事件中,onDragOver默认会阻止后续的drop/dragEnd事件触发,必须先调用e.preventDefault()才能正常完成拖拽流程。
核心问题4:dataset取值为字符串类型
dataset中取出的所有值都是字符串格式,直接传给splice作为索引会导致逻辑异常,需要先转成数字类型。
修复后的代码
GridHeader组件修改
import React, {useState} from "react"; export function GridHeader({ headerData, headerFunctionDispatcher, }) { const initDragInfos = { draggedItem: {}, draggedFromIndex: null, draggedToIndex: null, }; const [dragInfos, setDragInfos] = useState(initDragInfos); const newOnDragStart = (e, currentItemId) => { const currentItemIndex = headerData.findIndex( (item) => item.id === currentItemId ); // 改用函数式更新,保证拿到最新的状态 setDragInfos(prev => ({ ...prev, draggedItem: headerData[currentItemIndex], // 直接用计算得到的currentItemIndex,不用取dataset更稳妥 draggedFromIndex: currentItemIndex, })); }; const newOnDragOver = (e) => { // 必须阻止默认行为,否则拖拽结束事件不会触发 e.preventDefault(); setDragInfos(prev => ({ ...prev, draggedToIndex: Number(e.currentTarget.dataset.position), })); }; const newOnDragEnd = (e) => { headerFunctionDispatcher(e, "dragDropCol", dragInfos); setDragInfos(initDragInfos); }; return ( <thead> <tr> {/* map时传入index,绑定到data-position属性 */} {headerData.map((item, index) => { return( <th key={"grid-header-" + item.id} id={item.id} draggable data-position={index} onDragStart={(e) => newOnDragStart(e, item.id)} onDragOver={(e) => newOnDragOver(e)} onDragEnd={(e) => newOnDragEnd(e)} > {item.name} </th> ) })} </tr> </thead> ); }
headerFunctionDispatcher逻辑优化
const [header, setHeader] = useState(GRID_HEADER); const headerFunctionDispatcher = (e, name, options) => { e.preventDefault(); switch (name) { case "dragDropCol": // 加边界判断,避免索引为空时报错 if (options.draggedFromIndex === null || options.draggedToIndex === null) return; setHeader((prevHeader) => { let newHeader = [...prevHeader]; const draggedItem = newHeader.splice(options.draggedFromIndex, 1)[0]; newHeader.splice(options.draggedToIndex, 0, draggedItem); return newHeader; }); break; default: console.log("To be created"); } };
内容的提问来源于stack exchange,提问作者JohnWick
相关产品推荐
相关产品推荐

