react-dnd例子代码学习记录

例子代码仓库: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用法)

最外层

index.tsx

引入了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)

中间层

Container.tsx

数据源和数组交换的代码在这里。

  1. 数组交换时使用了useCallback。在依赖不变的情况下,会返回相同的引用,避免子组件进行无意义的重复渲染。useCallback缓存方法的引用。

    useCallback(函数,依赖项)

    const click = useCallback(() => {
    console.log(value)
    }, [value])

    当依赖项发生改变时,该方法会重新创建更新。

    内部的value值会随之更新,否则value永远是初始值

  2. 引入了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]
        }
    }
})

核心层

Card.tsx

使用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({参数})

  1. 返回值:[collect方法返回的对象,ref引用]

    第一个值是collect方法返回的对象。

    第二个值是ref引用,将其赋值给想要拖拽的元素就可以实现组件拖动。

  2. 参数

  • 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({参数})

  1. 返回值:[collect方法返回的对象,ref引用]

    第一个值是collect方法返回的对象。

    第二个值是ref引用,将其赋值给想要接收drag组件的元素,就可以感应到拖动的元素。

  2. 参数

  • 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. 让组件既可以被拖动也可以接收拖动元素

(1)使用useRef引入ref:ref = useRef(null)

(2)使用drag和drop包装ref:drag(drop(ref))

(3)将ref变量传给组件

  1. drag组件传递数据

(法1)直接使用item的属性传:item:{type:'Card‘,id:1}

(法2)使用begin方法传值,begin方法的返回值会覆盖item属性,一定要传type属性

drop组件可以在hover或drop方法中的第一个参数获取到,或使用DropTartgetMonitor的getItem()函数获取

  1. 获取drag组件或drop组件的状态信息,如isOver,isDragging
  • drag:collect函数返回的对象会成为useDrag的第一个参数collectProps,可以在组件中直接使用
  • drop:collect函数返回的对象会成为useDrop的第一个参数collectProps,可以在组件中直接使用

文件其他涉及方法

getBoundingClientRect用于获取某个元素相对于视窗的位置集合

hover方法中拖动比较

image-20210203100323859

posted @ 2021-02-03 10:00  wattmelon  阅读(849)  评论(0编辑  收藏  举报