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

React拖拽功能中更新selectedCard state后handleDrop获取值为null如何解决?

React拖拽中handleDrop无法获取最新selectedCard状态的解决办法

问题描述

我用React构建拖拽功能的Web应用时遇到一个问题:触发handleDragStart更新selectedCard状态后,在handleDrop里访问这个变量始终输出null。

原代码

import { useState } from "react";

const Panel = ({ data }) => {
  const { title, label, items } = data;
  const [selectedCard, setSelectedCard] = useState(null);

  const handleDragStart = (item) => {
    setSelectedCard(item);
  };
  const handleDrop = (colName, id) => {
    console.log(selectedCard);
  };

  const handleDragOver = (e) => {
    e.preventDefault();
  };

  return (
    <div className="w-56">
      <h2 className="mb-4">{title}</h2>
      <ul className="flex flex-col space-y-4">
        {items.map((item) => (
          <li key={item.id}>
            <button
              id={item.id}
              className="px-4 py-2 border w-full text-left cursor-grab"
              onDragStart={() => handleDragStart(item)}
              onDrop={() => handleDrop(label, item.id)}
              onDragOver={handleDragOver}
              draggable
            >
              {item.name}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Panel;

问题原因

  1. React的useState更新是异步的,handleDragStart中调用setSelectedCard后,状态不会立刻更新。
  2. 拖拽是浏览器原生事件流程,整个拖拽过程中组件不会触发重新渲染,handleDrop函数会捕获定义时的闭包,因此拿到的始终是初始的selectedCard值(null)。

解决方案

方案一:使用useRef保存选中数据

useRef的.current属性是可变的,不受闭包限制,能实时获取最新值:

import { useState, useRef } from "react";

const Panel = ({ data }) => {
  const { title, label, items } = data;
  const [selectedCard, setSelectedCard] = useState(null);
  const selectedCardRef = useRef(null);

  const handleDragStart = (item) => {
    setSelectedCard(item);
    selectedCardRef.current = item; // 同步更新ref
  };
  const handleDrop = (colName, id) => {
    console.log(selectedCardRef.current); // 从ref获取最新值
  };

  const handleDragOver = (e) => {
    e.preventDefault();
  };

  return (
    <div className="w-56">
      <h2 className="mb-4">{title}</h2>
      <ul className="flex flex-col space-y-4">
        {items.map((item) => (
          <li key={item.id}>
            <button
              id={item.id}
              className="px-4 py-2 border w-full text-left cursor-grab"
              onDragStart={() => handleDragStart(item)}
              onDrop={() => handleDrop(label, item.id)}
              onDragOver={handleDragOver}
              draggable
            >
              {item.name}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Panel;

方案二:利用原生拖拽的dataTransfer传递数据

这是原生拖拽API的标准用法,直接通过事件对象传递数据,不依赖React状态:

import { useState } from "react";

const Panel = ({ data }) => {
  const { title, label, items } = data;
  const [selectedCard, setSelectedCard] = useState(null);

  const handleDragStart = (e, item) => {
    e.dataTransfer.setData('text/plain', JSON.stringify(item)); // 存入序列化后的对象
    setSelectedCard(item);
  };
  const handleDrop = (e, colName, id) => {
    e.preventDefault(); // 必须调用,否则浏览器默认行为会阻止drop事件
    const selectedItem = JSON.parse(e.dataTransfer.getData('text/plain')); // 取出并解析数据
    console.log(selectedItem);
  };

  const handleDragOver = (e) => {
    e.preventDefault();
  };

  return (
    <div className="w-56">
      <h2 className="mb-4">{title}</h2>
      <ul className="flex flex-col space-y-4">
        {items.map((item) => (
          <li key={item.id}>
            <button
              id={item.id}
              className="px-4 py-2 border w-full text-left cursor-grab"
              onDragStart={(e) => handleDragStart(e, item)} // 传递事件对象
              onDrop={(e) => handleDrop(e, label, item.id)} // 传递事件对象
              onDragOver={handleDragOver}
              draggable
            >
              {item.name}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Panel;

方案对比

  • 方案一:代码改动小,适合单组件内的简单拖拽场景。
  • 方案二:符合原生拖拽API设计,支持跨组件、甚至跨页面的拖拽,通用性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 13:52:28