原文:https://dev.to/codewithsadee/how-to-build-a-task-management-app-with-react-typescript-106o(作者 @codewithsadee)
想不想做一个类似 Trello 或 Jira 的任务看板,能把卡片随意挪动?这篇教程就从零开始手写一个。做完的效果是:点住一张任务卡片,把它拖过屏幕,松手放进一个全新的列里。
最妙的一点是:整个过程不下载任何重量级拖拽库,只使用浏览器原生自带的拖拽能力。
项目结构与配置
先看项目文件,整套配置追求的就是快、省事:
- 用 Vite 跑本地开发服务器,对应 vite.config.ts 文件。
- 代码用 TypeScript 编写,相关规则集中在 tsconfig.json 里。
- 包管理交给 pnpm,它会生成 pnpm-lock.yaml 文件。
- 为了让界面保持整洁,按钮、输入框、文本域这些 UI 组件统一放进一个 UI 文件夹,另外还有一个 theme-provider.tsx 文件负责管理配色主题。
让任务卡片可拖动
要实现拖拽,先写一个 TaskCard.tsx 组件。想让一个元素可拖动,只需在 HTML 标签上加上 draggable 这个属性,然后通过 onDragStart 事件告诉浏览器“现在拖着的是什么”。
任务卡片的核心代码如下:
// src/components/TaskCard.tsx
export default function TaskCard({ task }) {
// 按下鼠标开始拖动的那一刻执行
const handleDragStart = (event) => {
// 保存任务 ID,让浏览器记住我们手里拿的是哪个任务
event.dataTransfer.setData("taskId", task.id);
};
return (
<div
draggable
onDragStart={handleDragStart}
className="p-4 mb-3 bg-white rounded shadow cursor-grab"
>
<p>{task.title}</p>
</div>
);
}
在新列里接住任务
任务拖起来了,还得有地方放下。为此再写一个 DropBox.tsx 组件,并把它放进主组件 Column.tsx 里。
一个合格的放置区要挂两个事件:onDragOver 和 onDrop。这里最关键的一个技巧是:悬停时必须阻止浏览器的默认行为(preventDefault),否则浏览器根本不允许你把元素放下来。
放置区的写法如下:
// src/components/DropBox.tsx
export default function DropBox({ onTaskDropped }) {
// 告诉浏览器:"这里允许放置元素!"
const handleDragOver = (event) => {
event.preventDefault();
};
// 松开鼠标按键时执行
const handleDrop = (event) => {
// 取出之前保存的任务 ID
const droppedTaskId = event.dataTransfer.getData("taskId");
// 把这个 ID 传给应用,让它去更新界面
onTaskDropped(droppedTaskId);
};
return (
<div
onDragOver={handleDragOver}
onDrop={handleDrop}
className="w-full min-h-[200px] p-4 bg-gray-100 rounded-lg"
>
{/* 任务卡片会显示在这里 */}
</div>
);
}
添加新任务
光能拖还不够,还得能录入新的待办事项。为此再做一个 TaskCreateField.tsx 组件,在里面输入你的下一个点子,直接添加到看板上。完整的界面效果和更多细节,可以参考作者的配套视频演示。
原文:https://dev.to/codewithsadee/how-to-build-a-task-management-app-with-react-typescript-106o(作者 @codewithsadee)



