使用 React 从头开始​​拖放看板🔥

2025-06-07

使用 React 从头开始​​拖放看板🔥

我们都使用那些看板风格的待办事项或任务管理应用程序,其中每个任务阶段都有列,我们可以将任务从一列拖放到另一列。

在 React 中实现这个拖放功能比我想象的要简单得多。你需要的一切都已经准备好了,无需使用任何库或包。那就让我们开始吧!🚀

***(使用了 Typescript,但在代码示例中并不明显,JS 用户可以轻松理解,并且代码示例中省略了 CSS 样式。请访问github 链接查看完整代码。)

这个想法💡

其背后的核心思想非常简单。我们将使用默认的 HTML 拖放 API,该 API 会使用各种拖放事件。要使任何 HTML 元素可拖动,只需为元素添加 draggable 属性即可。然后,我们将维护一个状态,用于存储所有拖放元素的数据,并将它们显示在 div 中。为此,我们将使用 HTML5 和 React 中现有的各种拖放事件监听器。

🎯 这里的主要技巧在于我们不能直接移动元素/组件。我们会在拖动时传递其数据,然后根据传递的数据在放置元素/组件的 div 中重新创建元素/组件。

我们将使用如下事件监听器:

  1. OnDrop:当有效的可拖动元素被放置在其区域时,此事件激活。
  2. OnDragStart:当开始拖动元素时触发此事件。
  3. OnDragEnd:拖动结束时触发。
  4. OnDragOver:当我们拖动元素区域时触发此事件

还有很多,但对于我们来说,这些已经足够了。那么,让我们开始实现吧。

实施

让我们在 div 中创建一些任务元素,然后将它们拖到其中:

任务 div

以及我们将要放置它们的 todo div:

图片描述

现在,为了使任务元素可拖动,我们只需添加可拖动属性。



<div
    className="..."
    draggable
>
    Task 1
</div>


Enter fullscreen mode Exit fullscreen mode

现在我们可以拖动任务 div 了。拖动元素时,我们需要传递其数据,以便重新创建它。在本例中,我们将传递其名称。

为了做到这一点,让我们使用onDragStart当我们开始拖动元素时触发的操作。



<div
    className="..."
    draggable
    onDragStart={(e) => {
        handleOnDrag(e, "Task 1");
    }}
>
    Task 1
</div>


Enter fullscreen mode Exit fullscreen mode

这里我们将事件 e 和任务名称传递给handleOnDrag函数。

让我们写这个handleOnDrag函数



function handleOnDrag(e: React.DragEvent, name: string) {
    e.dataTransfer.setData("name", name);
}


Enter fullscreen mode Exit fullscreen mode

这里我们将名称字符串设置为"name"键下的数据。现在,无论何时拖动它,名称数据都会随之移动。

我们已经处理了拖动部分,现在让我们将其放下。

我们需要一个状态来存储所有被删除任务的数据,然后在我们删除它们的 div 中显示它们。



const [tasks, setTasks] = useState<string[]>()


Enter fullscreen mode Exit fullscreen mode

现在我们将使用onDrop属性来触发 dropdown 事件。但在执行此操作之前,我们必须处理onDragOver一个特殊情况。只需执行以下操作:



<div 
    className="..."
    onDragOver={handleOnDragOver}
>
    {/* The div we are dropping the task in */}
</div>


Enter fullscreen mode Exit fullscreen mode

现在handleOnDragOver函数



function handleOnDragOver(e: React.DragEvent) {
    e.preventDefault();
}


Enter fullscreen mode Exit fullscreen mode

这将停止 onDragOver 不断触发的默认行为。

现在onDrop



<div 
    className="..."
    onDragOver={handleOnDragOver}
    onDrop={handleOnDrop}
>
    {/* The div we are dropping the task in */}
</div>


Enter fullscreen mode Exit fullscreen mode

现在让我们编写handleOnDrop函数



function handleOnDrop(e: React.DragEvent) {
    if(tasks) {
        setTasks([...tasks,e.dataTransfer.getData("name")] )
    } else {
        setTasks([e.dataTransfer.getData("name")])
    }
}


Enter fullscreen mode Exit fullscreen mode

这里我们创建了一个包含拖拽任务 div 数据的新数组,并将该数组设置为tasks状态。这e.dataTransfer.getData("name")部分看起来很熟悉。因为我们e.dataTransfer.setData("name", name)handleOnDrag函数中用它来命名被拖拽的任务。现在我们获取的是之前开始拖拽时设置的名称数据。

现在,我们已经获得了要拖放的任务的数据。接下来,我们只需使用获取的数据在 div 中显示该任务即可。我们将使用之前创建的 Task div,并将其拖放到 div 中。我们将映射该状态中的所有任务tasks,并逐一显示它们。



<div 
    className="..."
    onDragOver={handleOnDragOver}
    onDrop={handleOnDrop}
>
    {tasks &&
        tasks.map((taskName) => {
            return (
                <div
                    className="..."
                    draggable
                    onDragStart={(e) => {
                        handleOnDrag(e, taskName);
                    }}
                >
                    {taskName}
                </div>
            );
        })}
</div>


Enter fullscreen mode Exit fullscreen mode

最后,我们将拖动的任务放入了我们放置它的 div 中。

图片描述

但有一个问题。每次我们将任务拖放到 div 中时,任务都会重复。

图片描述

为了解决这个问题,我们必须在移动任务时删除该任务的上一个实例。这很容易做到filter()



function handleOnDrop(e: React.DragEvent) {
    if(tasks) {
        setTasks([
            ...tasks.filter(
                (taskName) => taskName !== e.dataTransfer.getData("name")
            ),
            e.dataTransfer.getData("name"),
        ]);
    } else {
        setTasks([e.dataTransfer.getData("name")])
    }
}


Enter fullscreen mode Exit fullscreen mode

现在一切正常了。我们可以将任务从列表中拖放到 Todo div 中。

图片描述

如何进一步扩展这个想法➡️

基本内容已设置完毕。现在,我们可以通过在开始拖动任务和放下任务时提供视觉反馈来使其更加用户友好。为此,我们可以使用onDragStart事件来更改元素的 CSS 样式。我们还可以添加动画。onDragEndonDragOver

在第二部分中,我们将完成所有这些工作,并添加两列用于显示正在进行的任务和已完成的任务,从而构建一个功能齐全的看板式待办事项板。我们需要管理三个独立的状态以及更多复杂功能。第二部分即将推出✅

第 2 部分已发布🔥

点击这里查看

完整代码链接Github 链接

我希望这对你有帮助😊在TwitterLinkedIn
上关注我 感谢阅读😇

文章来源:https://dev.to/nasif2ahmed/drag-and-drop-kanban-board-from-scratch-with-react-1j9a
PREV
如何使用 dev.to API!为什么?开始吧!
NEXT
您的第一个 React GraphQL 前端与 Apollo 客户端:简介、创建新的 React 应用、添加 GraphQL 到 Apollo 客户端、查询 API、更新变量、总结