React DnD实战总结:从入门到精通的拖拽功能开发经验分享

欧阳怡博 交互 阅读 2,664
赞 67 收藏
二维码
手机扫码查看
反馈

又踩坑了,React DnD拖拽不生效

最近在项目里用React DnD实现一个文件拖拽上传的功能,结果发现拖拽完全不生效。我一开始以为是配置问题,折腾了半天发现根本不是那么回事。

React DnD实战总结:从入门到精通的拖拽功能开发经验分享

排查过程,折腾了半天才发现的问题

首先,我检查了一下我的代码,确认所有依赖都安装好了,也看了好几遍官方文档,没发现什么明显的问题。然后我又去网上搜了一圈,发现大家遇到的问题都差不多,但解决方法五花八门。我试了好几种方法,比如换不同版本的React DnD库,修改Webpack配置,甚至尝试了不同的浏览器,但都没什么用。

后来我无意中发现了一个小细节,原来是我的CSS样式里有个user-select: none;,这个属性导致了拖拽事件无法触发。我一查资料,发现确实有这个问题,user-select: none;会阻止默认的拖拽行为。哎,真是个坑啊。

最终解决方案,核心代码就这几行

解决了user-select: none;的问题后,拖拽功能终于正常了。下面是完整的代码示例:

import React, { useCallback, useState } from 'react';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

const FileUpload = () => {
  const [files, setFiles] = useState([]);

  const handleDrop = (e) => {
    e.preventDefault();
    const droppedFiles = e.dataTransfer.files;
    if (droppedFiles.length > 0) {
      setFiles((prevFiles) => [...prevFiles, ...Array.from(droppedFiles)]);
    }
  };

  const [{ isOver }, drop] = useDrop(() => ({
    accept: 'text/plain',
    drop: (item, monitor) => handleDrop(monitor.getEvent()),
    collect: (monitor) => ({
      isOver: !!monitor.isOver(),
    }),
  }));

  const [{ isDragging }, drag] = useDrag(() => ({
    type: 'file',
    item: { id: 'file' },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  }));

  return (
    <DndProvider backend={HTML5Backend}>
      <div ref={drop} style={{ height: '200px', border: '1px solid black', padding: '20px', backgroundColor: isOver ? 'lightblue' : 'white' }}>
        <p>拖拽文件到这里上传</p>
        <div ref={drag} style={{ padding: '10px', cursor: 'move' }}>拖拽这里</div>
        <ul>
          {files.map((file, index) => (
            <li key={index}>{file.name}</li>
          ))}
        </ul>
      </div>
    </DndProvider>
  );
};

export default FileUpload;

这里的关键点在于处理handleDrop事件和使用useDropuseDrag钩子来管理拖拽状态。user-select: none;一定要去掉,否则拖拽功能会失效。

技术细节和原理,这里注意我踩过好几次坑

React DnD的核心是通过useDraguseDrop钩子来管理拖拽行为。useDrag负责处理拖拽源,useDrop负责处理拖拽目标。这两个钩子都需要返回一个对象,定义拖拽类型、拖拽项和收集器函数。

useDrop中,accept属性指定了可以接受的拖拽类型,drop函数则处理实际的拖拽操作。collect函数用于收集拖拽状态,比如是否正在拖拽(isOver)。

还有一个常见的坑是关于dataTransfer对象的使用。dataTransfer对象包含了拖拽过程中传递的数据,可以通过e.dataTransfer.files获取到拖拽的文件列表。如果拖拽的是文本或其他类型的数据,可以使用e.dataTransfer.getData('text/plain')等方法获取。

最后,我还发现了一个小技巧,就是在<DndProvider>组件中使用HTML5Backend作为后端,这样可以确保拖拽功能在浏览器中的兼容性。

总结一下,希望对你有帮助

以上是我踩坑后的总结,希望对你有帮助。如果你有更好的方案或者遇到类似的问题,欢迎评论区交流。这个技巧的拓展用法还有很多,后续我会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论