开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
如果接触过像 Jira, Trello 这种任务处理工具的话,一定对组件拖拽功能不陌生,一般它们的任务面板或者列表等都会支持这项功能,这让用户使用起来会非常方便,那么本文就将基于 React 世界中最流行的拖拽组件库 react-beautiful-dnd 来实现组件的拖拽功能。
React 18 实现的坑
因为实现这个项目用的最新版的 React 18,结果发现怎么都没有拖拽的效果,上网查了一下才发现有一些坑,根组件 App 不能被 React.StrictMode 包裹,否则就不起效,下面是实现这个功能的库版本
"react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.2.0"
对于这个问题可以参考 github 上这个 issue github.com/atlassian/r…
react-beautiful-dnd 库介绍
react-beautiful-dnd 是目前最流行的 React 拖拽开源库,github 上有28k的 Star 数,通过使用它可以很方便的实现拖拽功能。它的主要特性有:
- 高度可定制
- 不需要创建额外的 DOM 节点
- 提供了一系列的选项和元数据,让使用者可以任意的组合
它提供了三个组件,分别是:
DragDropContext
这个组件是用来包裹 Droppable 的,通过 React 的 Context 机制,它能有所有拖拽组件的信息,它可以接收几个回调函数,必须要实现的是 onDragEnd,后面会详细讲解这个回调的使用。
Droppable
这个组件在 DragDropContext 内部,包裹所有的可拖拽组件,它所定义的区域就是组件可以拖拽的区域。
Draggable
这个组件用来包裹可以拖拽的每个组件的,也就是 Item
这三者的关系用伪代码来说就是:
<DragDropContext>
<Droppable>
{
()=> list.map(() => <Draggable/>)
}
<Droppable/>
<DragDropContext/>
开始实现拖拽功能
安装 react-beautiful-dnd 库
npm install react-beautiful-dnd --save
创建 DragAndList 组件
首先,在项目中新建 DragAndList.tsx 组件
// DragAndList.tsx
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import ListItem from "./ListItem";
import {useState} from "react";
// 用来渲染列表的数据
const elements = [
{ id: "one", content: "我是第一" },
{ id: "two", content: "我是第二" },
{ id: "three", content: "我是第三" },
{ id: "four", content: "我是第四" }
];
// 这个就是拖拽后的回调事件
const onDragEnd = (result: any) => {
const newItems = Array.from(items);
const [removed] = newItems.splice(result.source.index, 1);
newItems.splice(result.destination.index, 0, removed);
setItems(newItems);
};
// 这里就是拖拽组件了
return <DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
<ListItem
provided={provided}
item={item}
/>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
上面的代码有几点需要注意的地方:
- onDragEnd 回调函数,需要创建后传递给 DragDropContext,因为 react-beautiful-dnd 这个库只负责了动画部分,而不负责处理数据,如果不定义这个回调函数的话,那拖拽的效果是有的,但是拖拽后还是会回到原位,也就是数据没有变,所以这里需要在这个函数中改变数据。
- Droppable 组件中 droppableId 是必传的,而且需要是唯一的
- Droppable 组件的 Children 是一个函数,而且必须要返回 React Element 元素
- ...provided.droppableProps 这些传参,包括后面的 ListItem 中类似的传参,一定要写
创建 ListItem 组件
接下来再创建 ListItem 组件
import {Card} from "antd";
const ListItem = ({item, provided}: any) => {
return <div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<Card style={{margin: 20}}>
<p>{item.content}</p>
</Card>
</div>
}
export default ListItem
这个组件比较简单,就是定义了每个 Item 而已。
在 App 组件中引入 DragAndList 组件
import DragAndList from "./DragAndList";
function App() {
return (
<div style={{backgroundColor: 'lightGrey',width: 500,padding: 20,borderRadius: 5,height: 800}}>
<DragAndList/>
</div>
)
}
export default App
实现的效果如下:
总结
react-beautiful-dnd 实现拖拽功能还是非常灵活的,而且更难得的是它侵入的代码很少,只是一些拖拽的动画实现,可以非常灵活的让使用者来自己定义渲染样式,以及怎样处理数据,并且构建出很强大的可拖拽组件。