You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 22:33:03