This article explains the REact-DND API and its usage in detail, and attached a Demo for reference, hoping to provide some help to friends in need.


A concept,

React DnD is a group of React high-level components. When using React DnD, you only need to use the corresponding API to wrap the target component to realize the function of dragging or accepting drag elements. Convert the drag event to the state of the object. You don’t need to determine the state of the drag. You just need to handle the state properties in the passed spec object. It may be difficult to understand at first, but it will be very convenient when you are really familiar with the usage.

This article Demo address: React-ndD-dustbin. If it helps, please welcome Star.


2. DragSource: Enables components to be dragged

Wrap the component around a DragSource so it can be dragged.

use

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

const spec = {
	beginDrag(props, monitor, component) {
		// Return the property of the object to be selected by itself
		return { id: props.id } } endDrag(props, monitor, component) { ... } canDrag(props, monitor) { ... } isDragging(props, monitor) { ... }}const collect = (connect, monitor) = > ({
	// Returns an object and assigns its properties to the component's props. These attributes need to be defined.
	connectDropTarget: connect.dropTarget(),
	id: monitor.getItem().id
})

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

export default MyComponent;
Copy the code

Explanation of parameters:

  • Type: required. String, ES6 symbol, or functions that return the given component. Only those registered for the same typedrop targetsWill react to the dragging source generated item
  • Spec: Required. A normal JavaScript object with some allowed methods. It describes how the drag source responds to drag-and-drop events.
  • Collect: Mandatory. Collection function. It should return a normal object to inject into your component. It receives two parameters: connect and monitor.
  • Options: Optional. A normal object.

Method in a spec object

  • BeginDrag (props, monitor, Component) : Mandatory. BeginDrag is called when the drag starts. You must return a pure JavaScript object that describes the data being dragged. The content you return is placed in the object obtained by monitor.getitem ().

  • EndDrag (props, monitor, component) : optional. EndDrag is called when the drag stops. For each beginDrag, endDrag will correspond.

  • CanDrag (props, monitor) : This is optional. Use it to specify whether dragging is currently allowed. If you want to always allow it, simply omit this method. Note: You may not be able to call this method monitor.candrag ().

  • IsDragging (props, monitor) : optional. By default, only a drag source that starts a drag operation is considered a drag. Note: You may not be able to call the monitor.isdragging () method.

Methods props, monitor, and component

  • props: of the current componentprops
  • monitorA:DragSourceMonitorInstance. Use it to query information about the current drag state, such as the item being dragged and its type, current and initial coordinates and offsets, and whether it has been deleted.
  • component: When specified, it is an instance of the component. Use it to access the underlying DOM node for position or size measurements, or to invokesetStateAnd other component methods.isDragging,canDragMethodcomponentBecause the instance may not be available when they are called

Connect and monitor parameters in collect

  • Connect: a DragSourceConnector instance. It comes in two ways: dragPreview() and dragSource().

    • dragSource() => (elementOrNode, options?) A common method that returns a function passed to the component to connect Source DOM to React DnD Backend
      • DragPreview () : Returns a function passed to the component to connect React DnD Backend to the DOM node it previews while dragging
  • Monitor: a DragSourceMonitor instance. This includes the following methods:

methods meaning
canDrag() Whether it can be dragged. Returns true if no drag operation is in progress
isDragging() Whether it is being dragged. Returns true if a drag operation is in progress
getItemType() Returns a string or ES6 symbol that identifies the type of the currently dragged item. Returns if the item is not draggednull
getItem() Returns a normal object representing the currently dragged item. Each drag source must specify it by returning an object from its beginDrag () method. Returns if the item is not draggednull
getDropResult() Returns the placement representing the last recorddrop resultobject
didDrop() If adrop targetDeal with thedropEvent, return true, otherwise return false. Even if thetargetThere is no returndropAs a result,didDrop()It also returns true. inendDrag()Is used to test whether any placed target has handled the drop. If theendDrag()If not called, returns false
getInitialClientOffset() Returns {x, y} from the pointer at the start of the current drag operationclientThe offset. Returns if the item is not draggednull
getInitialSourceClientOffset() Returns to the beginning of the current drag operationdrag source{x, y} for the component’s root DOM nodeclientThe offset. Returns if the item is not draggednull
getClientOffset() Return the last record of the pointer {x, y} while the drag operation is in progressclientThe offset. Returns if the item is not draggednull
getDifferenceFromInitialOffset() Returns the last record of the mouse at the beginning of the current drag operationclientThe offset andclientThe {x, y} difference between offsets. Returns if the item is not draggednull
getSourceClientOffset() returndrag sourceExpected {x, y} for the component’s root DOM nodeclientOffset based on its position at the start of the current drag operation and the movement difference. Returns if the item is not draggednull

DropTarget: Enables components to drop drag components

Wrap a component around a DropTarget to react to a drag, hover, or dropped compatible item.

use

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

const spec = {
	drop(props, monitor, component) {
		// Return the property of the object to be selected by itself
		return { id: props.id } } hover(props, monitor, component) { ... } canDrop(props, monitor) { ... }}const collect = (connect, monitor) = > ({
	// Returns an object and assigns its properties to the component's props. These attributes need to be defined.
	connectDropTarget: connect.dropTarget()
})

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

Explanation of parameters:

  • Type: required. String, ES6 symbol, or functions that return the given component. This place target is only for the specified typedrag sourcesProject response
  • Spec: Required. A normal JavaScript object with some allowed methods. It describes how the drop target responds to the drag and drop event.
  • Collect: Mandatory. Collection function. It should return a normal item object to inject into your component. It receives two parameters: connect and monitor.
  • Options: Optional. A normal object.

Method in a spec object

  • Drop (props, monitor, component) : This is optional. Called when a compatible item is placed on the target. You can return undefined or a normal object. If an object is returned, it will be the result of the placement, which can be obtained using monitor.getDropresult ().

  • Hover (props, monitor, component) : optional. Called when the project hovers over the component. You can check monitor.isover ({shallow: true}) to test whether the hover occurs only on the current target or nested.

  • CanDrop (props, monitor) : This is optional. Use it to specify whether the project can be accepted by the place target. If you want to always allow it, simply omit this method.

The document does not provide a way to process the entry or exit events for their purpose. Instead, monitor.isover () returns the result of the call from the collection function so that we can use the componentDidUpdateReact hook function to handle the entry and exit events in the component.

Methods props, monitor, and component

  • props: of the current componentprops
  • monitorA:DropTargetMonitorInstance. Use it to query information about the current drag state, such as the item being dragged and its type, current and initial coordinates and offsets, whether the current target is exceeded, and whether it can be deleted.
  • component: When specified, it is an instance of the component. Use it to access the underlying DOM node for position or size measurements, or to invokesetStateAnd other component methods.canDragMethodcomponentBecause the instance may not be available when they are called.

Connect and monitor parameters in collect

  • Connect: a DropTargetConnector instance. It has only one dropTarget() method.

    • dropTarget() => (elementOrNode)A common method that returns a function passed to the component to connect target DOM to React DnD Backend. Any React element can be marked as a placeable node by {connectDropTarget: connect.dropTarget()} returned from the collection function.
  • Monitor: A DropTargetMonitor instance. This includes the following methods:

methods meaning
canDrop() Whether it can be placed. Returns true if a drag operation is in progress
isOver(options) drag sourceWhether to hover overdrop targetArea. You can choose to pass{ shallow: true }To strictly check if onlydrag sourceHover instead of nesting targets
getItemType() Returns a string or ES6 symbol that identifies the type of the currently dragged item. Returns if the item is not draggednull
getItem() Returns a normal object representing the current drag item, which each drag source must specify by returning an object from its beginDrag() method. Returns if the item is not draggednull
getDropResult() Returns the placement representing the last recorddrop resultobject
didDrop() If adrop targetDeal with thedropEvent, return true, otherwise return false. Even if thetargetThere is no returndropAs a result,didDrop()It also returns true. inendDrag()Is used to test whether any placed target has handled the drop. If theendDrag()If not called, returns false
getInitialClientOffset() Returns {x, y} from the pointer at the start of the current drag operationclientThe offset. Returns if the item is not draggednull
getInitialSourceClientOffset() Returns to the beginning of the current drag operationdrag source{x, y} for the component’s root DOM nodeclientThe offset. Returns if the item is not draggednull
getClientOffset() Return the last record of the pointer {x, y} while the drag operation is in progressclientThe offset. Returns if the item is not draggednull
getDifferenceFromInitialOffset() Returns the last record of the mouse at the beginning of the current drag operationclientThe offset andclientThe {x, y} difference between offsets. Returns if the item is not draggednull
getSourceClientOffset() returndrag sourceExpected {x, y} for the component’s root DOM nodeclientOffset based on its position at the start of the current drag operation and the movement difference. Returns if the item is not draggednull

DragDropContext & DragDropContextProvider

Note: Components that use the DragSource and DropTarget packages must be placed inside either the root component of the: DragDropContext package or the root tag of the DragDropContextProvider.

DragDropContext

Use DragDropContext to wrap the root component of your application to enable React DnD.

usage

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;
Copy the code

parameter

  • Backend: Mandatory. A React DnD backend. Unless you are writing a custom one, it is recommended to use HTML5Backend, which comes with React DnD.

  • Context: Backend depends. Context object used to customize the backend. For example, HTML5Backend can inject custom window objects for iframe scenarios.

DragDropContextProvider

As an alternative to DragDropContext, you can use the DragDropContextProvider element to enable React DnD for your application. Similar to DragDropContext, this can be injected into the backend via BackendProp, but a Window object can also be injected.

usage

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>)}}Copy the code

parameter

  • Backend: Mandatory. A React DnD backend. Unless you are writing a custom one, it is recommended to use HTML5Backend, which comes with React DnD.

  • Context: Backend depends. Context object used to customize the backend. For example, HTML5Backend can inject custom window objects for iframe scenarios.


A simple example of React-DND

Refer to the official Dustbin example for this example.

Project preparation

The current project is built using create-React-app scaffolding and is written using decorator syntax when using React-Dnd. So you need to add some configuration to your project first.

For the decorator enabled configuration, see my previous article: Enable decorator syntax in create-React-App.

Create a Components folder to hold the components you wrote. Create a types folder to store type string constants, and create an index.js file in the types directory to declare the corresponding type value.

types/index.js

export default {
	BOX: 'box'
}
Copy the code

So the current project SRC directory file structure is as follows:

SRC ├── components/ ├── types/ ├─ index.js ├── app.js ├─ index.css ├─ index.jsCopy the code

Create the Box component as the DragSource

In the Components directory, create the box.js file and write the Box component so that it can be dragged

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.5 rem 1 rem'.marginRight: '1.5 rem'.marginBottom: '1.5 rem'.cursor: 'move'.float: 'left',}const boxSource = {
	@param {*} props */ props */
	beginDrag(props) {
		// The returned object can be obtained in monitor.getitem ()
		return {
			name: props.name,
		}
	},

	@param {*} props props for the current component * @param {*} Monitor DragSourceMonitor object */
	endDrag(props, monitor) {
		// The item component being dragged
		const item = monitor.getItem()
		// Drop the element, drop the result
		const dropResult = monitor.getDropResult()

		// If the drop result exists, an alert is displayed
		if (dropResult) {
			alert(`You dropped ${item.name} into ${dropResult.name}! `)
		}
	},
}

@DragSource(
	// Type id, here is the string 'box'
	ItemTypes.BOX,
	// Drag the event object
	boxSource,
	// Collect function functions, including connect and monitor parameters
	// Connect connects DOM nodes to React-dnd backend
	(connect, monitor) => ({
		// Wrap the DOM node so that it can be dragged and dropped
		connectDragSource: connect.dragSource(),
		// Whether it is in the drag state
		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

		// Wrap the DOM node with connectDragSource so that it can accept various drag apis
		// connectDragSource can drag only DOM nodes wrapped by connectDragSource
		return connectDragSource && connectDragSource(
				<div style={{ . style.opacity}} >
					{name}
				</div>); }}export default Box;
Copy the code

Create a Dustbin component as a DropTarget

In the Components directory, create a dustbin. js file and write a Dustbin component so that it can accept the corresponding drag-and-drop component.

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.5 rem'.marginBottom: '1.5 rem'.color: 'white'.padding: '1rem'.textAlign: 'center'.fontSize: '1rem'.lineHeight: 'normal'.float: 'left',}const boxTarget = {
	// When a corresponding drag source is placed in the current component area, an object is returned, which can be obtained in monitor.getDropresult ()
	drop: (a)= > ({ name: 'Dustbin' })
}

@DropTarget(
	// Type id, here is the string 'box'
	ItemTypes.BOX,
	// Receive drag-and-drop event objects
	boxTarget,
	// Collect function functions, including connect and monitor parameters
	// Connect connects DOM nodes to React-dnd backend
	(connect, monitor) => ({
		// Wrap the DOM node so that it can receive the corresponding drag component
		connectDropTarget: connect.dropTarget(),
		// Drag whether the source is in the drop Target area
		isOver: monitor.isOver(),
		// Can be placed
		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';
		// When the component is in the Drag target area, the background color of the current component changes to darkgreen
		if (isActive) {
			backgroundColor = 'darkgreen';
		} 
		// When the current component can place the Drag source, the background color changes to pink
		else if (canDrop) {
			backgroundColor = 'darkkhaki';
		}

		// Wrap the DOM node with a connectDropTarget so that it can receive the corresponding Drag Source component
		// Only DOM nodes wrapped by connectDropTarget can receive drag Source components
		return connectDropTarget && connectDropTarget(
			<div style={{ . style.backgroundColor}} >
				{isActive ? 'Release to drop' : 'Drag a box here'}
			</div>); }}export default Dustbin;
Copy the code

Use DragDropContext in the app.js file

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';

// Pass HTMLBackend as argument to 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;
Copy the code

Run the project to see the effect

Running projects:

$ npm run start
Copy the code

The browser will automatically open the http://localhost:3000/ window. At this time, you can operate the Box component on the browser and view the effect combined with the project code. The preview looks like this:


6. Address of Demo

react-dnd-dustbin

Welcome to Star! Thank you very much!


Vii. Reference links

React DND Official document drag component: Use of React DND