Подробное объяснение использования react-dnd

React.js

В этой статье подробно объясняется API и использование react-dnd, а также прилагается демонстрация для справки, в надежде оказать помощь друзьям, которые в ней нуждаются.


1. Концепция

React DnD — это набор высокоуровневых компонентов React, при его использовании вам нужно только использовать соответствующий API для обертывания целевого компонента, чтобы реализовать функцию перетаскивания или принятия перетаскиваемых элементов. Чтобы преобразовать событие перетаскивания в соответствующее состояние объекта, разработчику не нужно самостоятельно оценивать состояние перетаскивания, а нужно только выполнить соответствующую обработку в каждом атрибуте состояния в переданном объекте спецификации. Это может быть трудно понять при первом контакте, но это будет очень удобно после того, как вы действительно освоитесь с использованием.

эта статьяDemoадрес:react-dnd-dustbin. Если полезно, добро пожаловать в Star.


2. DragSource: позволяет перетаскивать компоненты

использоватьDragSourceОберните компонент так, чтобы его можно было перетаскивать.

Как пользоваться

import React, { Component } from 'react';
import { DragSource } from 'react-dnd';

const spec = {
	beginDrag(props, monitor, component) {
		// 这里 return 出去的对象属性自行选择,这里只是用 id 作为演示
		return { id: props.id }
	}

	endDrag(props, monitor, component) {
			...
	}

	canDrag(props, monitor) {
			...
	}

	isDragging(props, monitor) {
			...
	}
}

const collect = (connect, monitor) => ({
	// 这里返回一个对象,会将对象的属性都赋到组件的 props 中去。这些属性需要自己定义。
	connectDropTarget: connect.dropTarget(),
	id: monitor.getItem().id
})

@DragSource(type, spec, collect)
class MyComponent extends Component {
  /* ... */
}

export default MyComponent;

Объяснение параметра:

  • Тип: обязательный. Строка, нотация ES6 или свойства функции, которые возвращают данный компонент. Зарегистрированы только для того же типаdrop targetsбудет реагировать на элементы, созданные этим источником перетаскивания
  • Спецификация: требуется. Простой объект JavaScript с некоторыми разрешенными методами. Он описывает, как источник перетаскивания реагирует на события перетаскивания.
  • собирать: обязательно. функция сбора. Он должен возвращать простой объект для внедрения в ваш компонент. Он получает два параметра: connect и monitor.
  • варианты: опционально. обычный предмет.

методы в объекте спецификации

  • beginDrag(props, monitor, component): Необходимые. Когда начинается перетаскивание,beginDragназывается. Вы должны вернуть чистыйJavaScriptобъект. Контент, который вы вернете, будет помещен вmonitor.getItem()в полученном объекте.

  • endDrag(props, monitor, component): по желанию. Когда перетаскивание останавливается,endDragназывается. для каждогоbeginDrag,endDragбудет соответствовать.

  • canDrag(props, monitor): по желанию. Используйте его, чтобы указать, разрешено ли в данный момент перетаскивание. Если вы хотите всегда разрешать это, просто пропустите этот метод. Примечание: возможно, вы не сможете позвонитьmonitor.canDrag()Этот метод.

  • isDragging(props, monitor): по желанию. По умолчанию только источник перетаскивания, который инициирует операцию перетаскивания, считается перетаскиванием. Примечание: возможно, вы не сможете позвонитьmonitor.isDragging()Этот метод.

Параметры в методах props, monitor, component

  • props: текущий компонентprops
  • monitor:ОдинDragSourceMonitorпример. Используйте его для запроса информации о текущем состоянии перетаскивания, такой как текущий перетаскиваемый элемент и его тип, текущие и начальные координаты и смещения, а также был ли он удален.
  • component: Если указано, это экземпляр компонента. Используйте его для доступа к базовым узлам DOM для измерения положения или размера или для вызоваsetStateи другие компонентные методы.isDragging,canDragметод недоступенcomponentэтот параметр, потому что экземпляр может быть недоступен при их вызове

подключать и контролировать параметры в сборе

  • connect: ОдинDragSourceConnectorпример. У него есть два метода: dragPreview() и dragSource().

    • dragSource() => (elementOrNode, options?): Общий метод, возвращает функцию, переданную компоненту для подключения исходного DOM и React DnD Backend.
      • dragPreview(): возвращает функцию, которая передается компоненту для подключения узла DOM, предварительно просматриваемого при перетаскивании, с бэкэндом React DnD.
  • монитор: одинDragSourceMonitorпример. Содержит следующие методы:

метод значение
canDrag() Можно перетащить. true, если операция перетаскивания не выполняется
isDragging() тащат. true, если выполняется операция перетаскивания
getItemType() Возвращает строку или символ ES6, идентифицирующий тип перетаскиваемого в данный момент элемента. Возвращает, если ни один элемент не был перетащенnull
getItem() Возвращает простой объект, представляющий текущий перетаскиваемый элемент. Каждый источник перетаскивания должен указывать его, возвращая объект из своего метода beginDrag(). Возвращает, если ни один элемент не был перетащенnull
getDropResult() Возвращает место размещения, представляющее последнюю записьdrop resultобъект
didDrop() еслиdrop targetобработанныйdropсобытие, возвращает true, иначе возвращает false. Несмотря на тоtargetбез возвратаdropрезультат,didDrop()также вернет true. существуетendDrag()Используйте его, чтобы проверить, обработала ли какая-либо цель перетаскивания. если вendDrag()Вызывается извне, возвращает false
getInitialClientOffset() Возвращает {x, y} указателя в начале текущей операции перетаскивания.clientКомпенсировать. Возвращает, если ни один элемент не был перетащенnull
getInitialSourceClientOffset() Возвращает, когда текущая операция перетаскивания началасьdrag source{x, y} корневого узла DOM компонентаclientКомпенсировать. Возвращает, если ни один элемент не был перетащенnull
getClientOffset() Возвращает последний записанный {x, y} указателя во время выполнения операции перетаскивания.clientКомпенсировать. Возвращает, если ни один элемент не был перетащенnull
getDifferenceFromInitialOffset() Возвращает последнюю запись мыши, когда началась текущая операция перетаскивания.clientсмещение сclient{x, y} разница между смещениями. Возвращает, если ни один элемент не был перетащенnull
getSourceClientOffset() возвращениеdrag sourceОжидаемый {x, y} корневого узла DOM компонентаclientСмещение, основанное на его положении в начале текущей операции перетаскивания и разнице в движении. Возвращает, если ни один элемент не был перетащенnull

3. DropTarget: Включить компоненты для размещения компонентов перетаскивания и падения

использоватьDropTargetОберните компонент так, чтобы он реагировал на перетаскивание, наведение или опускание совместимых элементов.

Как пользоваться

import React, { Component } from 'react';
import { DropTarget } from 'react-dnd';

const spec = {
	drop(props, monitor, component) {
		// 这里 return 出去的对象属性自行选择,这里只是用 id 作为演示
		return { id: props.id }
	}

	hover(props, monitor, component) {
			...
	}

	canDrop(props, monitor) {
			...
	}
}

const collect = (connect, monitor) => ({
	// 这里返回一个对象,会将对象的属性都赋到组件的 props 中去。这些属性需要自己定义。
	connectDropTarget: connect.dropTarget()
})

@DropTarget(type, spec, collect)
class MyComponent extends Component {
	/* ... */
}
export default MyComponent;

Объяснение параметра:

  • Тип: обязательный. Строка, нотация ES6 или свойства функции, которые возвращают данный компонент. Эта цель перетаскивания действительна только для определенных типовdrag sourcesпроект отвечает
  • Спецификация: требуется. Простой объект JavaScript с некоторыми разрешенными методами. Он описывает, как цель перетаскивания реагирует на события перетаскивания.
  • собирать: обязательно. функция сбора. Он должен возвращать обычный объект реквизита для внедрения в ваш компонент. Он принимает два параметра: connect и monitor.
  • варианты: опционально. обычный предмет.

методы в объекте спецификации

  • drop(props, monitor, component): по желанию. Вызывается, когда на цель помещается совместимый предмет. может вернутьсяundefinedили обычные предметы. Если объект возвращается, он будет результатом перетаскивания и может использоваться сmonitor.getDropResult()полученный.

  • hover(props, monitor, component): по желанию. Вызывается при наведении элемента на компонент. ты можешь проверитьmonitor.isOver({ shallow: true })чтобы проверить, происходит ли наведение только на текущей цели или вложенной.

  • canDrop(props, monitor): по желанию. Используйте это, чтобы указать, может ли цель перетаскивания принять элемент. Если вы хотите всегда разрешать это, просто пропустите этот метод.

Документация не предоставляет способ обработки входных или выходных событий намеренно. ноmonitor.isOver()Верните результат вызова из функции сбора, чтобы мы могли использоватьcomponentDidUpdateReactХуки-функции для обработки событий входа и выхода в компоненте.

Параметры в методах props, monitor, component

  • props: текущий компонентprops
  • monitor:ОдинDropTargetMonitorпример. Используйте его для запроса информации о текущем состоянии перетаскивания, такой как текущий перетаскиваемый элемент и его тип, текущие и начальные координаты и смещения, находится ли он за текущей целью и можно ли его удалить.
  • component: Если указано, это экземпляр компонента. Используйте его для доступа к базовым узлам DOM для измерения положения или размера или для вызоваsetStateи другие компонентные методы.canDragметод недоступенcomponentЭтот параметр может быть недоступен для экземпляра, когда они вызываются.

подключать и контролировать параметры в сборе

  • connect: ОдинDropTargetConnectorпример. это только одинdropTarget()метод.

    • dropTarget() => (elementOrNode): общий метод, возвращает функцию, которая передается компоненту для подключения целевого DOM к React DnD Backend. Любой элемент React можно пометить как удаляемый узел с помощью { connectDropTarget: connect.dropTarget() }, возвращаемого из функции сбора.
  • монитор: одинDropTargetMonitorпример. Содержит следующие методы:

метод значение
canDrop() могут быть размещены. Возвращает true, если выполняется операция перетаскивания
isOver(options) drag sourceстоит ли наводить курсорdrop targetплощадь. дополнительная доставка{ shallow: true }строго проверять, только лиdrag sourceнаведение, а не вложенные цели
getItemType() Возвращает строку или символ ES6, идентифицирующий тип перетаскиваемого в данный момент элемента. возврат, если ни один элемент не был перетащенnull
getItem() Возвращает простой объект, представляющий текущий пункт перетаскивания, каждый источник сопротивления должен указывать его, возвращая объект из своего метода PUGINDRAG (). возврат, если ни один элемент не был перетащенnull
getDropResult() Возвращает место размещения, представляющее последнюю записьdrop resultобъект
didDrop() еслиdrop targetобработанныйdropсобытие, возвращает true, иначе возвращает false. Несмотря на тоtargetбез возвратаdropрезультат,didDrop()также вернет true. существуетendDrag()Используйте его, чтобы проверить, обработала ли какая-либо цель перетаскивания. если вendDrag()Вызывается извне, возвращает false
getInitialClientOffset() Возвращает {x, y} указателя в начале текущей операции перетаскивания.clientКомпенсировать. Возвращает, если ни один элемент не был перетащенnull
getInitialSourceClientOffset() Возвращает, когда текущая операция перетаскивания началасьdrag source{x, y} корневого узла DOM компонентаclientКомпенсировать. Возвращает, если ни один элемент не был перетащенnull
getClientOffset() Возвращает последний записанный {x, y} указателя во время выполнения операции перетаскивания.clientКомпенсировать. Возвращает, если ни один элемент не был перетащенnull
getDifferenceFromInitialOffset() Возвращает последнюю запись мыши, когда началась текущая операция перетаскивания.clientсмещение сclient{x, y} разница между смещениями. Возвращает, если ни один элемент не был перетащенnull
getSourceClientOffset() возвращениеdrag sourceОжидаемый {x, y} корневого узла DOM компонентаclientСмещение, основанное на его положении в начале текущей операции перетаскивания и разнице в движении. Возвращает, если ни один элемент не был перетащенnull

В-четвертых, DragDropContext и DragDropContextProvider.

Уведомление:Компоненты, обернутые DragSource и DropTarget, должны быть размещены: внутри корневого компонента, обернутого DragDropContext, или внутри корневого тега DragDropContextProvider.

DragDropContext

использоватьDragDropContextОберните корневой компонент приложения, чтобы включить React DnD.

использование

import React, { Component } from 'react';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';

@DragDropContext(HTML5Backend)
class YourApp extends Component {
  /* ... */
}

export default YourApp;

параметр

  • бэкэнд: требуется. Бэкенд React DnD. Если вы не пишете собственный, рекомендуется использовать HTML5Backend, который поставляется с React DnD.

  • контекст: бэкэнд-зависимость. Объект контекста для пользовательских бэкендов. Например, HTML5Backend может вставлять пользовательские объекты окна для сцен iframe.

DragDropContextProvider

в видеDragDropContextВ качестве альтернативы вы можете использоватьDragDropContextProviderElement включает React DnD для приложения. иDragDropContextТочно так же это можно сделать с помощьюbackendpropвнедрить серверную часть, но также может внедритьwindowобъект.

использование

import React, { Component } from 'react';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContextProvider } from 'react-dnd';

export default class YourApp extends Component {
	render() {
		return (
			<DragDropContextProvider backend={HTML5Backend}>
			/* ... */
			</DragDropContextProvider>
		)
	}
}

параметр

  • бэкэнд: требуется. Бэкенд React DnD. Если вы не пишете собственный, рекомендуется использовать HTML5Backend, который поставляется с React DnD.

  • контекст: бэкэнд-зависимость. Объект контекста для пользовательских бэкендов. Например, HTML5Backend может вставлять пользовательские объекты окна для сцен iframe.


5. Простой пример реакции-dnd

Этот пример объясняется со ссылкой на официальный пример мусорной корзины.

Подготовка проекта

Текущий проект используетcreate-react-appЛеса построены и используютсяreact-dndвсегда записываются с использованием синтаксиса декоратора. Поэтому сначала вам нужно добавить некоторую конфигурацию в проект.

Способ настройки включения декоратора можно найти в моей предыдущей статье:Включить синтаксис декоратора в приложении create-реагировать.

новыйcomponentsПапка, используемая для хранения записанных компонентов. новыйtypesпапка для храненияtypeСтроковые константы, вtypesСоздано в каталогеindex.jsсоответствует объявлению файлаtypeценность.

types/index.js

export default {
	BOX: 'box'
}

Итак, текущий проектsrcСтруктура файлов в каталоге следующая:

src
├── components/
├── types/
      └── index.js
├── App.js
├── index.css
└── index.js

Создайте компонент Box как DragSource

существуетcomponentsкаталог, создатьBox.jsфайл, пишиBoxкомпонент, чтобы сделать его перетаскиваемым

components/Box.js

import React from 'react';
import PropTypes from 'prop-types';
import { DragSource } from 'react-dnd';

import ItemTypes from '../types';

const style = {
	border: '1px dashed gray',
	backgroundColor: 'white',
	padding: '0.5rem 1rem',
	marginRight: '1.5rem',
	marginBottom: '1.5rem',
	cursor: 'move',
	float: 'left',
}

const boxSource = {
	/**
	 * 开始拖拽时触发当前函数
	 * @param {*} props 组件的 props
	 */
	beginDrag(props) {
		// 返回的对象可以在 monitor.getItem() 中获取到
		return {
			name: props.name,
		}
	},

	/**
	 * 拖拽结束时触发当前函数
	 * @param {*} props 当前组件的 props
	 * @param {*} monitor DragSourceMonitor 对象
	 */
	endDrag(props, monitor) {
		// 当前拖拽的 item 组件
		const item = monitor.getItem()
		// 拖拽元素放下时,drop 结果
		const dropResult = monitor.getDropResult()

		// 如果 drop 结果存在,就弹出 alert 提示
		if (dropResult) {
			alert(`You dropped ${item.name} into ${dropResult.name}!`)
		}
	},
}

@DragSource(
	// type 标识,这里是字符串 'box'
	ItemTypes.BOX,
	// 拖拽事件对象
	boxSource,
	// 收集功能函数,包含 connect 和 monitor 参数
	// connect 里面的函数用来将 DOM 节点与 react-dnd 的 backend 建立联系
	(connect, monitor) => ({
		// 包裹住 DOM 节点,使其可以进行拖拽操作
		connectDragSource: connect.dragSource(),
		// 是否处于拖拽状态
		isDragging: monitor.isDragging(),
	}),
)
class Box extends React.Component {

	static propTypes = {
		name: PropTypes.string.isRequired,
		isDragging: PropTypes.bool.isRequired,
		connectDragSource: PropTypes.func.isRequired
	}

	render() {
		const { isDragging, connectDragSource } = this.props
		const { name } = this.props
		const opacity = isDragging ? 0.4 : 1

		// 使用 connectDragSource 包裹住 DOM 节点,使其可以接受各种拖动 API
		// connectDragSource 包裹住的 DOM 节点才可以被拖动
		return connectDragSource && connectDragSource(
				<div style={{ ...style, opacity }}>
					{name}
				</div>
			);
	}
}

export default Box;

Создайте компонент мусорной корзины как DropTarget

существуетcomponentsкаталог, создатьDustbin.jsфайл, пишиDustbinкомпонент, чтобы он мог принять соответствующий компонент перетаскивания.

components/Dustbin.js

import React from 'react';
import PropTypes from 'prop-types';

import { DropTarget } from 'react-dnd';
import ItemTypes from '../types';

const style = {
	height: '12rem',
	width: '12rem',
	marginRight: '1.5rem',
	marginBottom: '1.5rem',
	color: 'white',
	padding: '1rem',
	textAlign: 'center',
	fontSize: '1rem',
	lineHeight: 'normal',
	float: 'left',
}

const boxTarget = {
	// 当有对应的 drag source 放在当前组件区域时,会返回一个对象,可以在 monitor.getDropResult() 中获取到
	drop: () => ({ name: 'Dustbin' })
}

@DropTarget(
	// type 标识,这里是字符串 'box'
	ItemTypes.BOX,
	// 接收拖拽的事件对象
	boxTarget,
	// 收集功能函数,包含 connect 和 monitor 参数
	// connect 里面的函数用来将 DOM 节点与 react-dnd 的 backend 建立联系
	(connect, monitor) => ({
		// 包裹住 DOM 节点,使其可以接收对应的拖拽组件
		connectDropTarget: connect.dropTarget(),
		// drag source是否在 drop target 区域
		isOver: monitor.isOver(),
		// 是否可以被放置
		canDrop: monitor.canDrop(),
	})
)
class Dustbin extends React.Component {

    static propTypes = {
        canDrop: PropTypes.bool.isRequired,
        isOver: PropTypes.bool.isRequired,
        connectDropTarget: PropTypes.func.isRequired
    }

	render() {
		const { canDrop, isOver, connectDropTarget } = this.props;
		const isActive = canDrop && isOver;

		let backgroundColor = '#222';
		// 拖拽组件此时正处于 drag target 区域时,当前组件背景色变为 darkgreen
		if (isActive) {
			backgroundColor = 'darkgreen';
		} 
		// 当前组件可以放置 drag source 时,背景色变为 pink
		else if (canDrop) {
			backgroundColor = 'darkkhaki';
		}

		// 使用 connectDropTarget 包裹住 DOM 节点,使其可以接收对应的 drag source 组件
		// connectDropTarget 包裹住的 DOM 节点才能接收 drag source 组件
		return connectDropTarget && connectDropTarget(
			<div style={{ ...style, backgroundColor }}>
				{isActive ? 'Release to drop' : 'Drag a box here'}
			</div>
		);
	}
}

export default Dustbin;

Используйте DragDropContext в файле App.js

App.js

import React, { Component } from 'react';
import { DragDropContext } from 'react-dnd';
import HTMLBackend from 'react-dnd-html5-backend';

import Dustbin from './components/Dustbin';
import Box from './components/Box';

// 将 HTMLBackend 作为参数传给 DragDropContext
@DragDropContext(HTMLBackend)
class App extends Component {
  render() {
    return (
        <div style={{ paddingLeft: 200, paddingTop: 50 }}>
            <div style={{ overflow: 'hidden', clear: 'both' }}>
                <Box name="Glass" />
                <Box name="Banana" />
                <Box name="Paper" />
            </div>
            <div style={{ overflow: 'hidden', clear: 'both' }}>
                <Dustbin />
            </div>
        </div>
    );
  }
}

export default App;

Запустите проект, чтобы увидеть эффект

Запустите проект:

$ npm run start

Браузер автоматически откроетсяhttp://localhost:3000/окно, вы можете работать с компонентом Box в браузере, комбинировать код проекта и просматривать эффект. Эффект предварительного просмотра выглядит следующим образом:

预览效果


6. Демонстрационный адрес этой статьи

react-dnd-dustbin

Добро пожаловать Звезду! Спасибо!


7. Справочная ссылка

официальная документация react-dnd Перетаскивание компонентов: использование React DnD