持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情
前言
react拖拽库有很多:react-dnd、react-draggable、react-beautiful-dnd、smooth-dnd;但是他们各有各的优缺点;
react-beautiful-dnd交互做的很好,性能优化到了极致;但是无法单独设置一个只可以往外拖拽的元素,扩展性比较差
react-draggable功能比较局限
react-dnd只提供底层API,我们还是需要去处理复杂数据交换,扩展性高,但是理解和使用起来非常复杂,它的作者同时也是redux的作者
smooth-dnd是一个关注度很小的一个库,但是它功能齐全
我们今天就选择最难的react-dnd介绍一下,因为看过文档都不一定会用,而其他几个库基本上照着文档操作就行了
首先需要安装两个依赖:npm i -S react-dnd react-dnd-html5-backend
Provider
首先我们回忆一下redux的用法:需要先在最外层包裹一个Provider,类似的react-dnd也需要这样操作:
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'
import {Component} from 'react'
export default class Layout extends Component {
render() {
retuen(
<div class='layout'>
<DndProvider backend={HTML5Backend}>{this.props.children}</DndProvider>
</div>
)
}
}
react-dnd有hooks API和class API两种,但是class API难以理解而且用起来十分反人类,所以我们就不介绍了
hooks API
useDrag
我们看一个这个函数的签名:
第一个参数:item,item.type指定拖拽的ID,到时候到useDrag里面也需要使用,它是一个唯一拖拽ID,只有ID匹配的元素才能拖放
begin函数:参数是DragSourceMonitor,这个对象里面可以拿到所有拖拽元素的数据,我们可以做一些状态初始化的工作;对应的是dragstart事件,如果有返回值那么这个值会传递到drop和dragend
useDrag({
begin:()=>{
dispatch({
type:'init_drag'
})
return {
type:'drag_id'
}
}
})
end函数:对应dragend事件,可以拿到drop的结果,拖拽结束,做最后的状态变更
返回值:[CollectedProps, ConnectDragSource, ConnectDragPreview]
CollectedProps:这个返回值和useDrag中的collect参数有关,我们之前没有介绍这个参数,因为传进去再返回出来好像用处不大
ConnectDragSource:这个参数是指定拖拽元素的,很重要
const ref = useRef(null)
const [,useDragRef] = useDrag()
useDragRef(ref)
// 需要拖拽的元素
return <div ref={ref}/>
ConnectDragPreview:预览用的
useDrop
accept:对应useDrag中的item.type
collect:收集属性,这里有一个额外作用是“组织冒泡”
useDrop({
collect: (monitor: DropTargetMonitor) => ({
canDrop: monitor.canDrop(),
isOverCurrent: monitor.isOver({ shallow: true })
}),
})
canDrop:是否可以放置
hover:其实是dragEnter,会反复触发,容易引发性能问题;在这里我们可以采用diff操作避免重复触发的,因为每一个dragitem都有一个唯一id,如果说hover上去的key与当前高亮的key相同那么我们直接退出,这样就减少了页面的重排重绘
drop:触发drop事件,在dragend之前
总结
本文简单介绍了一下react-dnd的使用以及在使用过程中需要组织冒泡以及防止hover反复执行的问题;如果觉得react-dnd用的不顺手可以试试其他的拖拽组件,适合项目的才是最好的