使用 React 从头开始拖放看板🔥
我们都使用那些看板风格的待办事项或任务管理应用程序,其中每个任务阶段都有列,我们可以将任务从一列拖放到另一列。
在 React 中实现这个拖放功能比我想象的要简单得多。你需要的一切都已经准备好了,无需使用任何库或包。那就让我们开始吧!🚀
***(使用了 Typescript,但在代码示例中并不明显,JS 用户可以轻松理解,并且代码示例中省略了 CSS 样式。请访问github 链接查看完整代码。)
这个想法💡
其背后的核心思想非常简单。我们将使用默认的 HTML 拖放 API,该 API 会使用各种拖放事件。要使任何 HTML 元素可拖动,只需为元素添加 draggable 属性即可。然后,我们将维护一个状态,用于存储所有拖放元素的数据,并将它们显示在 div 中。为此,我们将使用 HTML5 和 React 中现有的各种拖放事件监听器。
🎯 这里的主要技巧在于我们不能直接移动元素/组件。我们会在拖动时传递其数据,然后根据传递的数据在放置元素/组件的 div 中重新创建元素/组件。
我们将使用如下事件监听器:
- OnDrop:当有效的可拖动元素被放置在其区域时,此事件激活。
- OnDragStart:当开始拖动元素时触发此事件。
- OnDragEnd:拖动结束时触发。
- OnDragOver:当我们拖动元素区域时触发此事件
还有很多,但对于我们来说,这些已经足够了。那么,让我们开始实现吧。
实施
让我们在 div 中创建一些任务元素,然后将它们拖到其中:
以及我们将要放置它们的 todo div:
现在,为了使任务元素可拖动,我们只需添加可拖动属性。
<div
className="..."
draggable
>
Task 1
</div>
现在我们可以拖动任务 div 了。拖动元素时,我们需要传递其数据,以便重新创建它。在本例中,我们将传递其名称。
为了做到这一点,让我们使用onDragStart
当我们开始拖动元素时触发的操作。
<div
className="..."
draggable
onDragStart={(e) => {
handleOnDrag(e, "Task 1");
}}
>
Task 1
</div>
这里我们将事件 e 和任务名称传递给handleOnDrag
函数。
让我们写这个handleOnDrag
函数
function handleOnDrag(e: React.DragEvent, name: string) {
e.dataTransfer.setData("name", name);
}
这里我们将名称字符串设置为"name"
键下的数据。现在,无论何时拖动它,名称数据都会随之移动。
我们已经处理了拖动部分,现在让我们将其放下。
我们需要一个状态来存储所有被删除任务的数据,然后在我们删除它们的 div 中显示它们。
const [tasks, setTasks] = useState<string[]>()
现在我们将使用onDrop
属性来触发 dropdown 事件。但在执行此操作之前,我们必须处理onDragOver
一个特殊情况。只需执行以下操作:
<div
className="..."
onDragOver={handleOnDragOver}
>
{/* The div we are dropping the task in */}
</div>
现在handleOnDragOver
函数
function handleOnDragOver(e: React.DragEvent) {
e.preventDefault();
}
这将停止 onDragOver 不断触发的默认行为。
现在onDrop
<div
className="..."
onDragOver={handleOnDragOver}
onDrop={handleOnDrop}
>
{/* The div we are dropping the task in */}
</div>
现在让我们编写handleOnDrop
函数
function handleOnDrop(e: React.DragEvent) {
if(tasks) {
setTasks([...tasks,e.dataTransfer.getData("name")] )
} else {
setTasks([e.dataTransfer.getData("name")])
}
}
这里我们创建了一个包含拖拽任务 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>
最后,我们将拖动的任务放入了我们放置它的 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")])
}
}
现在一切正常了。我们可以将任务从列表中拖放到 Todo div 中。
如何进一步扩展这个想法➡️
基本内容已设置完毕。现在,我们可以通过在开始拖动任务和放下任务时提供视觉反馈来使其更加用户友好。为此,我们可以使用、onDragStart
等事件来更改元素的 CSS 样式。我们还可以添加动画。onDragEnd
onDragOver
在第二部分中,我们将完成所有这些工作,并添加两列用于显示正在进行的任务和已完成的任务,从而构建一个功能齐全的看板式待办事项板。我们需要管理三个独立的状态以及更多复杂功能。第二部分即将推出✅
第 2 部分已发布🔥
点击这里查看
完整代码链接:Github 链接
我希望这对你有帮助😊在Twitter和LinkedIn
上关注我 感谢阅读😇