例子代码仓库:react-dnd
参考文档:
一直以来
useCallback
的使用姿势都不对-普拉斯强(useCallback的用法)React Hooks 解析(下):进阶-dabai(useCallback的用法)
react hooks 之 useCallback ,useMemo-饭饭大人(useCallback的用法)
React性能优化:immutability-helper-weixin_34216196(immutability-helper的用法)
[转] immutability-helper 插件的基本使用(附源码)-{前端开发}(immutability-helper的用法)
react-dnd拖拽页面记录-对鸭(将react-dnd用法分四部分)
用 React Hooks 的方式使用 react-dnd-暖生(通过例子讲react-dnd用法)
最外层
引入了DndProvider[react-dnd]、HTML5Backend[react-dnd-html5-backend]
import React from 'react'
import ReactDOM from 'react-dom'
import { Container } from './Container'
import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'
function App() {
return (
<div className="App">
<DndProvider backend={HTML5Backend}>
<Container />
</DndProvider>
</div>
)
}
const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
中间层
数据源和数组交换的代码在这里。
-
数组交换时使用了useCallback。在依赖不变的情况下,会返回相同的引用,避免子组件进行无意义的重复渲染。useCallback缓存方法的引用。
useCallback(函数,依赖项)
const click = useCallback(() => {
console.log(value)
}, [value])当依赖项发生改变时,该方法会重新创建更新。
内部的value值会随之更新,否则value永远是初始值
-
引入了immutability-helper,以最低的成本对抗浅比较
immutability-helper用法
使用示例:
update(target_array, {score: {$push: [10]}}) // 返回新的对象
注意点:
$push、$unshift、$splice的使用目标必须是数组。
$add、$remove的使用目标必须是Set或Map。
$splice的参数是一个操作数组,可以对目标数组一次进行多次操作,但是参数arrays中的项是按顺序执行的。
可用命令:
{$push: array}
同数组的 push 方法,将参数 array 中的所有项 push 到目标数组中
{$unshift: array}
同数组的 unshift 方法,将参数 array 中的所有项 unshift 到目标数组中
{$splice: array of arrays}
同数组的 splice 方法,对于参数 arrays 中的每一项,使用该项提供的参数对目标数组调用 splice()array.splice(index,howmany,item1,......itemX)
{$set: any}
使用 any 值替换目标
{$toggle: array of strings}
将参数 array 中提供的下标或者属性的值切换成相反的布尔值
{$unset: array of strings}
从目标对象中移除参数 array 中的键列表
{$merge: object}
将参数 object 的键与目标合并
{$apply: function}
将当前值传递给函数并用新的返回值更新它
{$add: array of objects}
向 Set 或 Map 中添加值。添加到 Set 时,参数 array 为要添加的对象数组,添加到 Map 时,参数 array 为 [key, value] 数组
{$remove: array of strings}
从 Set 或 Map 中移除参数 array 中的键列表
扩展运算符与immutability-helper比较:
扩展运算符:
newState = {
...state,
score: {
...state.score,
exam2: [
...state.score.exam2,
90,
],
},
}
使用immutability-helper:
newState = update(state, {
score: {
exam2: {
$push: [90]
}
}
})
核心层
使用react-dnd的API
react-dnd用法
monitor
一个DragSourceMonitor / DropTargetMonitor实例。
// DragSourceMonitor
monitor.canDrag() // 是否能被拖拽
monitor.isDragging() // 是否正在拖拽
monitor.getItemType() // 拖拽组件type
monitor.getItem() // 当前拖拽的item
monitor.getDropResult() // 查询drop结果
monitor.didDrop() // source是否已经drop在target
monitor.getInitialClientOffset() // 拖拽组件初始拖拽时offset
monitor.getInitialSourceClientOffset()
monitor.getClientOffset() // 拖拽组件当前offset
monitor.getDifferenceFromInitialOffset() // 当前拖拽offset和初始拖拽offset的差别
monitor.getSourceClientOffset()
// DropTargetMonitor
monitor.canDrop() // 是否可被放置
monitor.isOver(options) // source是否在target上方
monitor.getItemType() // 拖拽组件type
monitor.getItem() // 当前拖拽的item
monitor.getDropResult() // 查询drop结果
monitor.didDrop() // source是否已经drop在target
monitor.getInitialClientOffset() // 拖拽组件初始拖拽时offset
monitor.getInitialSourceClientOffset()
monitor.getClientOffset() // 拖拽组件当前offset
monitor.getDifferenceFromInitialOffset() // 当前拖拽offset和初始拖拽offset的差别
monitor.getSourceClientOffset()
useDrag用于拖动
const [collect方法返回的对象,ref引用] = useDrag({参数})
-
返回值:[collect方法返回的对象,ref引用]
第一个值是collect方法返回的对象。
第二个值是ref引用,将其赋值给想要拖拽的元素就可以实现组件拖动。
-
参数
-
item
是一个对象,必须要有一个type属性(必填)item: { type: ItemTypes.CARD, 其他属性 },
-
begin(mintor:DragSourceMonitor)
组件开始拖动时触发,必须返回一个包含type属性的对象,会覆盖item属性返回的对象,会被传入drop组件hover和drop方法的第一个参数 -
end(item,mintor:DragSourceMonitor)
组件停止拖动时触发,item是drop组件在drop方法执行时返回的对象,等同于mintor.getDropResult()的值 -
canDrag
指定当前是否允许拖动 -
isDragging
默认情况下,只有启动拖动操作的数据源才被认为是拖动 -
collect(mintor:DropTargetMonitor)
函数,返回的对象会成为useDrag的第一个参数,可以在组件中直接进行使用 -
spec
普通对象
useDrop用于接收
const [collect方法返回的对象,ref引用] = useDrop({参数})
-
返回值:[collect方法返回的对象,ref引用]
第一个值是collect方法返回的对象。
第二个值是ref引用,将其赋值给想要接收drag组件的元素,就可以感应到拖动的元素。
-
参数
-
accept
是一个标识字符串,需要和对应的drag元素中item的type值一致,否则不能感应accept: ItemTypes.CARD
-
hover(item,mintor:DropTargetMonitor)
drag组件在drop组件上方hover时触发 -
drop(item,mintor:DropTargetMonitor)
drag组件拖拽结束后,放到drop组件时触发,返回的值会作为参数传递给drag组件end方法的第一个参数 -
collect(mintor:DropTargetMonitor)
函数,返回的对象会成为useDrop的第一个参数,可以在组件中直接进行使用 -
options
普通对象
相关用法
- 让组件既可以被拖动也可以接收拖动元素
(1)使用useRef引入ref:ref = useRef
(null) (2)使用drag和drop包装ref:drag(drop(ref))
(3)将ref变量传给组件
- drag组件传递数据
(法1)直接使用item的属性传:item:{type:'Card‘,id:1}
(法2)使用begin方法传值,begin方法的返回值会覆盖item属性,一定要传type属性
drop组件可以在hover或drop方法中的第一个参数获取到,或使用DropTartgetMonitor的getItem()函数获取
- 获取drag组件或drop组件的状态信息,如isOver,isDragging
- drag:collect函数返回的对象会成为useDrag的第一个参数collectProps,可以在组件中直接使用
- drop:collect函数返回的对象会成为useDrop的第一个参数collectProps,可以在组件中直接使用
文件其他涉及方法
getBoundingClientRect用于获取某个元素相对于视窗的位置集合