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

React函数组件实现拖拽时onDragOver无法访问state问题求助

问题根本原因

  • 闭包陷阱:React函数组件每次重渲染都会生成新的作用域,绑定的拖拽事件回调捕获的是首次渲染时的items state和局部变量快照,后续state更新后回调里拿到的还是旧值
  • 声明的draggedItem是普通局部变量,每次组件重渲染都会被重置为null,无法跨渲染周期保存值
  • 更新items时直接用了splice修改原state数组,违反了React不可变数据原则,会导致渲染异常

修复后的完整代码

import { useState, useRef } from 'react';

function MenuCreatorCourse() {
  const [items, setItems] = useState([1, 2]);
  // 用useRef存拖拽的元素,跨渲染周期保持值更新
  const draggedItemRef = useRef(null);

  function onDragStart(e, index) {
    draggedItemRef.current = items[index];
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/html", e.target.parentNode);
    e.dataTransfer.setDragImage(e.target.parentNode, 20, 20);
    console.log("draggedItem @ DragStart", draggedItemRef.current);
  }

  const onDragOver = (e, index) => {
    e.preventDefault();
    const draggedOverItem = items[index];
    console.log("@onDragOver", index, items);
    console.log("draggedOverItem", draggedOverItem);
    
    // 拖拽到自身直接返回
    if (draggedItemRef.current === draggedOverItem) {
      return;
    }

    // 按照不可变原则生成新数组,不要修改原state
    const newItems = [...items];
    // 先删除原位置的拖拽元素
    const draggedIndex = newItems.indexOf(draggedItemRef.current);
    newItems.splice(draggedIndex, 1);
    // 插入到目标位置
    newItems.splice(index, 0, draggedItemRef.current);
    setItems(newItems);
    console.log("draggedItem @ DragOver", draggedItemRef.current);
  };

  function onDragEnd() {
    // 拖拽结束清空ref
    draggedItemRef.current = null;
  }

  return (
    <ul onDragOver={(e) => e.preventDefault()}>
      {items.map((item, idx) => (
        <li
          className={classes.courseinput}
          key={item}
          onDragOver={(e) => onDragOver(e, idx)}
        >
          <div
            className="drag"
            draggable
            onDragStart={(e) => onDragStart(e, idx)}
            onDragEnd={onDragEnd}
          >
            <DragAndDropIcon />
          </div>
          <div></div>
          <div className="form-control">
            <input
              type="text"
              id="description"
              placeholder="Description"
            />
          </div>
          <div>
            <RemoveIcon />
          </div>
        </li>
      ))}
    </ul>
  );
}

export default MenuCreatorCourse;

核心修改点

  • 用useRef存储draggedItem,ref的current属性可以跨渲染周期保持最新值,不受闭包影响
  • 生成新数组时采用展开运算符复制原数组后再修改,不直接操作原state,符合React数据更新规范
  • 补充了onDragEnd逻辑,拖拽结束后清空存储的拖拽元素,避免后续逻辑异常

内容的提问来源于stack exchange,提问作者user2601660

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 07:36:10