之前写过一篇 react-dnd 用法的文章,里面写的可能比较啰嗦了,但是内容比较详细,很多 API 都罗列了出来。目前 React Hooks 出来了,react-dnd 也做了对应的更新,所以本篇使用 React Hooks + TypeSscript 对 react-dnd 用法重新梳理一下。
当前版本介绍
react
:
16.9.0
react-dom
:
16.9.0
typescript
:
3.5.3
react-dnd
:
9.3.4
react-dnd-html5-backend
:
9.3.4
脚手架使用的
create-react-app
,全局安装
create-react-app
的命令:
npm i create-react-app -g
npx create-react-app react-dnd-hooks --typescript
react-dnd
:
yarn add react-dnd
react-dnd-html5-backend
:
yarn add react-dnd-html5-backend
为了方便统一个标识,'拖拽组件' 使用 'drag 组件' 代替,'目标接收组件' 使用 'drop 组件' 代替
一、用 DndProvider 将根节点包裹起来
想要使用
react-dnd
进行拖拽操作,需要用
DndProvider
标签将根节点包裹起来,并传入一个
backend
参数:
index.tsx
文件
import React from 'react';
import ReactDOM from 'react-dom';
import { DndProvider } from 'react-dnd';
import HTMLBackend from 'react-dnd-html5-backend'
import './index.css';
import App from './App';
ReactDOM.render(
<DndProvider backend={ HTMLBackend }>
<App />
</DndProvider>,
document.getElementById('root'));
复制代码
二、让元素可以动起来
比如现在有一个 Box 组件,我们想将其进行拖拽,此时我们先声明一下这个组件
创建 Box 组件
Box.tsx
import React, { CSSProperties } from 'react';
const style: CSSProperties = {
width: 200,
height: 50,
lineHeight: '50px',
background: 'pink',
margin: '30px auto'
const Box = () => {
return (
<div style={ style }>可拖拽组件 Box</div>
export default Box;
复制代码
这个时候我们使用鼠标按住 Box 组件,发现还没有办法拖拽 Box 组件
让 Box 组件动起来
使用
react-dnd
提供的
useDrag
, 返回的第一个值是 collect 方法返回的对象(这里没有使用,所以省略了),返回的第二个值是一个 ref,将其赋值给想要拖拽的元素即可实现组件拖动。
Box.tsx
这个时候我们就可以使用鼠标来回拖拽
Box
组件了,但是只能拖拽,还没有地方能够接收感应到
Box
组件,下面我们来看看怎么操作。
三、创建 Dustbin 组件用来接收 drag 组件
使用
react-dnd
提供的
useDrop
,返回的第一个值是 collect 方法返回的对象,返回的第二个值是一个 ref,将其赋值给想要接收的
drop
组件即可感应到 drag 组件。
Dustbin
组件:
import React, { CSSProperties } from 'react';
import { useDrop, DropTargetMonitor } from 'react-dnd';
const style: CSSProperties = {
width: 400,
height: 400,
margin: '100px auto',
lineHeight: '60px',
border: '1px dashed black'
const Dustbin = () => {
// 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
const [collectProps, droper] = useDrop({
// accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
accept: 'Box',
// collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
collect: (minoter: DropTargetMonitor) => ({
isOver: minoter.isOver()
const bg = collectProps.isOver ? 'deeppink' : 'white';
const content = collectProps.isOver ? '快松开,放到碗里来' : '将 Box 组件拖动到这里'
return (
// 将 droper 赋值给对应元素的 ref
<div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
export default Dustbin;
温馨提示:记得将 Box 和 Dustbin 组件引用到 App.tsx 组件里使用。
四、效果图
五、其他常用 API 和注意事项
drag
组件常用的属性:
item
:是一个对象,必须要有一个 type
属性
begin(mintor: DragSourceMonitor)
:组件开始拖拽,必须返回一个对象包含 type
属性,会覆盖 item
属性返回的对象,会被传入 drop 组件 hover 和 drop 方法的第一个参数
end(item, mintor: DragSourceMonitor)
: 组件停止拖拽时触发,item
是 drop
组件在 drop
方法执行时返回的对象,等同于 mintor.getDropResult()
的值
const [, drag] = useDrag({
item: { type: 'Card', id: 1, name: 'card1', kind: 'Card }
利用 begin 方法传值:
注意:begin 方法的返回值会将 item 属性覆盖,所以一定要传 type 属性
const [, drag] = useDrag({
item: { type: 'Card' },
begin(mintor: DragSourceMonitor) {
return { type: 'Card', id: 1, name: 'card1', kind: 'Card }
之后在 dropTarget
接收组件中就可以在 hover 或者 drop 方法的第一个参数中获取到,或者使用 DropTargetMonitor
的 getItem()
函数获取。
想要获取到 drag
组件或者 drop
组件的一些状态信息
drag
组件:
// collect 函数返回的对象会赋给 useDrop 的第一个参数 collectProps,可以在组件中直接进行使用
const [collectProps, drag] = useDrag({
item: { type: 'Card', id: 1, name: 'card1', kind: 'Card },
collect: (minoter: DropTargetMonitor) => ({
isOver: minoter.isOver(),
drop
组件:
// collect 函数返回的对象会赋给 useDrop 的第一个参数 collectProps,可以在组件中直接进行使用
const [collectProps, droper] = useDrop({
accept: 'Box',
collect: (minoter: DropTargetMonitor) => ({
isOver: minoter.isOver(),
更多状态信息的 API 可以查看我之前文章 中的 DragSourceMonitor 相关 API 和 DropTargetMonitor 相关 API