React-DnD的简要使用方法与API文档
前提
这是一个便于开发者操作拖拽交互的库,第一次看的时候感觉非常抽象,捣鼓半天终于懂了那么一丢丢!它的官方文档贼难进去,这篇文章主要是把一些常用的内容记下,希望能帮助到大家。本篇文章参考的是16.0.1
版本
npm i react-dnd
1 简单示例
1.1 useDrag:让DOM允许拖拽
import React from 'react'
import { useDrag } from 'react-dnd'
export default function Player() {
// 第一个返回值是一个对象,主要放一些拖拽物的状态。后面会介绍,先不管
// 第二个返回值:顾名思义就是一个Ref,只要将它注入到DOM中,该DOM就会变成一个可拖拽的DOM
const [_, dragRef] = useDrag({
type: 'Player', // 给拖拽物命名,后面用于分辨该拖拽物是谁,支持string和symbol
item: { id:1 }, // 拖拽物所携带的数据,让后面一些事件可以拿到数据,已达到交互的目的
},[])
// 由上面示例可知, useDrag所接收的参数一个是对象/函数 和一个依赖数组(可选)
// 注入Ref,现在这个DOM就可以拖拽了
return (
<div ref={dragRef}></div>
)
}
1.2 useDrop:让DOM作为拖放区域
import { useDrop } from 'react-dnd'
export const Dustbin = () => {
const [_, dropRef] = useDrop({
accept: ['Player'], // 指明该区域允许接收的拖放物。可以是单个,也可以是数组
// 里面的值就是useDrag所定义的type
// 当拖拽物在这个拖放区域放下时触发,这个item就是拖拽物的item(拖拽物携带的数据)
drop: (item) => {},
})
// 将ref注入进去,这个DOM就可以处理拖拽物了
return (
<div ref={dropRef} ></div>
)
}
1.3. 组合:让DOM作为拖拽物和拖放区域
import React, { useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd';
const Player=({onChangePosition,id})=>{
const ref = useRef(null);
// 1. 让DOM作为拖拽物,允许拖放
const [_, drag] = useDrag(() => ({
type: 'PLAYER',
item: { id:1 }
}));
// 2. 让DOM作为可拖放的区域
const [, drop] = useDrop(() => ({
accept: ['PLAYER'],
drop(item) {
if (onChangePosition) {
onChangePosition(item.id,id);
}
}
}));
// 3. 让目标DOM即能作为拖拽物,也能作为拖放区域
drag(drop(ref));
// 4. 下面就是施展魔法,把ref注入DOM中
return <div ref={ref}></div>
}
下面就是枯燥的API搬运了,为了方便理解,以下内容中,由useDrag注册的DOM将称为拖拽物,由useDrop注册的DOM将称为拖放区域
2. useDrag
先放一个官网比较完整的示例:
import { useDrag } from 'react-dnd'
function DraggableComponent(props) {
// 返回三个值:
// collected:是一个对象,下面的collect函数返回什么,就有什么,常用于判断拖拽物的状态
// drag:拖拽物Ref
// dragPreview: 当DOM处于拖拽状态时,所显示的DOM的ref
const [{ isDragging, opacity }, drag, preview] = useDrag({
type: 'BOX',
item: { id:1 },
// 这个monitor会提供拖拽物状态的信息,我会在下面罗列所有monitor支持的方法
collect: (monitor) => ({
isDragging: monitor.isDragging(), // 是否处于拖拽状态
// 还有一种常用的写法: 直接根据状态去获取样式
opacity:monitor.isDragging()?0.5:1
}),
},[],
)
return collected.isDragging ? (
<div ref={preview} />
) : (
<div ref={drag} style={{opacity:isDragging?0.5:1}}>
...
</div>
)
}
2.1 返回值与参数对象
const [collected,dragRef,dragPreviewRef]=useDrag({
type:'Player',
item:{id:1},
previewOptions:{},
end(item, monitor){...}, // 拖拽停止时调用,item是拖拽物在上方定义的数据
canDrag(monitor){...},
isDragging(monitor){...},
collect(monitor ,props)=>({...})
},[])
返回值分别为:
- 第一个参数对象中collect函数返回的对象
- 拖拽物的引用
- 处于拖拽状态的拖拽物的引用
参数分别为:
-
一个特定对象,也可以是返回该特定对象的函数
-
依赖数组
特定对象的属性如下:
属性 描述 type 必填。string或symbol类型。拖放区域所接受的拖拽物将从这里挑选 item 必填。函数或对象。拖拽物携带的数据。如果传对象,不建议使用多层嵌套或复杂的对象 previewOptions 用于描述 dragPreviewRef
的对象end(item, monitor) 拖拽停止时调用。可在函数内调用 monitor.didDrop()
来判断该拖拽物是否被放在所定义的拖放区域中。如果拖放区域定义了drop()
,那么可以通过monitor.getDropResult()
去获取drop
返回的类型canDrag(monitor) 返回布尔值。指明该拖拽物是否允许拖拽。如果想一直允许拖拽,则可以忽略这个属性。(无法再内部调用 monitor.canDrag()
)isDragging(monitor) 返回布尔值。指明拖拽物是否处于拖拽状态。默认情况下,当拖拽物正拖拽时则被认为是处于拖拽状态。(无法再内部调用 monitor.isDragging()
)collect(monitor ,props) 返回对象的函数。可以从useDrag返回的第一个值获取
2.2 monitor 属性
上面出现了很多monitor,monitor的作用就是记录拖拽物状态信息,支持的属性如下:
属性 | 描述 |
---|---|
canDrag() | 如果没有拖拽操作正在进行,返回true; |
isDragging() | 如果有拖拽操作正在进行,返回true; |
getItemType() | 返回拖拽物的定义的type |
getItem() | 返回拖拽物的定义的item |
getDropResult() | 当拖放结束,返回拖放区域定义的drop() 所返回的对象 |
didDrop() | 如果拖放区域执行了drop() ,返回true,即便drop() 没有返回对象,也返回true |
getInitialClientOffset() | 返回拖拽开始时,拖拽物的起点位置 |
getInitialSourceClientOffset() | 返回拖拽开始时,拖拽物的根节点的位置 |
getClientOffset() | 当拖拽正在进行时,返回拖拽物再次过程中最后的位置 |
getDifferenceFromInitialOffset() | 返回最后记录的位置与起点位置的差值 |
getSourceClientOffset() | 返回拖拽物当前在根组件位置与拖拽物最初在根组件位置的差值 |
3. useDrop
const [collected,dropRef]=useDrop({
accept:'Player',
drop(item, monitor){...}, // 拖拽物拖拽进来是调用,item为拖拽物携带的数据
hover(item, monitor){...} // 拖拽物悬浮在拖放区域时,item为拖拽物携带的数据
canDrop(item, monitor){...}
collect(monitor ,props)=>({...})
},[])
3.1 参数对象
属性 | 描述 |
---|---|
accept | 必填。指定拖放区域所接受的拖拽物。string或symbol类型,或者是数组。 |
drop(item, monitor) | 当拖拽物被拖拽进来是调用。而如果函数返回对象,那么对应拖拽物的end 方法则可以通过monitor.getDropResult() 来获取这个对象 |
hover(item, monitor) | 当拖拽物悬浮在拖放区域时调用。 可以通过monitor.isOver({ shallow: true }) 来指定是否只悬浮在单个拖放区域上,而不悬浮在嵌套的拖放区域上 |
canDrop(item, monitor) | 返回布尔值。指明该拖放区域是否可用。如果是一直可用,忽略即可。 |
collect(monitor ,props) | 返回对象的函数。可以从useDrop返回的第一个值获取 |
3.2 monitor 属性
记录了拖放区域内拖拽物状态信息,支持的属性如下:
属性 | 描述 |
---|---|
canDrop() | 如果有拖拽操作正在进行,返回true; |
isOver(options) | 如果有拖拽操作正在进行并悬浮在拖放区域上,返回true;可传入{ shallow: true } 来指定是否只悬浮在该区域上,而不是悬浮在嵌套的拖放区域上 |
getItemType() | 返回当前拖进来的拖拽物定义的type |
getItem() | 返回当前拖进来的拖拽物定义的item |
getDropResult() | 返回当前拖放区域定义的drop()返回的对象 |
didDrop() | 如果拖放区域执行drop()操作,返回true |
4. 后话
另外,在写项目的过程中,也摸索了一个可拖拽和不可拖拽dom的快速切换方法,感兴趣的可以往下看~
ListItem.js
import React, { useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd';
// 首先写一个基本的组件
// 这里用到了React.forwardRef,目的是等下会把拖拽的引用注入进来
const Base = React.forwardRef(
(params,ref) => {
return (
<div ref={ref}>
基本组价的内容
</div>
);
}
);
// 接着再另一个组件注册拖拽,并将引用注入到Base组件中
const Drag = (props) => {
const ref = useRef(null);
const [_, drag] = useDrag(() => ({
type: 'LIST_ITEM',
item: { id:1 }
}));
const [, drop] = useDrop(() => ({
accept: ['LIST_ITEM'],
drop(item, monitor) {...}
}));
drag(drop(ref));
return <Base {...props} ref={ref}/>;
};
export default {
Base,
Drag
};
使用
import ListItem from './ListItem';
// 一般情况下
<ListItem.Base />
// 如果想要有拖拽功能
<ListItem.Drag />
本文作者:sanhuamao
本文链接:https://www.cnblogs.com/sanhuamao/p/17318203.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步