Introduction to the

Recently, WHEN I studied how to draw topology diagrams with React, I involved HTML5 drag-and-drop API, and learned about React DnD, a drag-and-drop wizard. React DnD provides a set of drag-and-drop apis that simplify the use of the drag-and-drop API. Here’s how to use React DnD:

Important concepts

React Dnd provides several important apis for us to use:

  • DragSource
  • DropTarget
  • DragDropContext && DragDropContextProvider

DragSource

DragSource is a higher-order component. Components wrapped with DragSource higher-order components can be dragged and dropped.

Basic usage:

import { DragSource } from 'react-dnd'

class MyComponent {
  / *... * /
}

export default DragSource(type, spec, collect)(MyComponent)
Copy the code

Parameters:

  • Type: Specifies the type of the drag element. The value can be string, symbol, or func. Only elements of the same type can be responded to by the drop target.

  • Spec: A JS object that defines methods to describe how drag source responds to drag events.

    • BeginDrag (props, monitor, Component): Mandatory. This method is called when the drag starts and must return a JS object describing the element being dragged, such as returning a{ id: props.id }Through themonitor.getItem()Method to get the return result.
    • EndDrag (props, monitor, Component): Optional. This method is called when the drag stops, passing throughmonitor.didDrop()Can be judgeddrag sourceHave you beendrop targetThe processing is complete. If thedrop targetdropMethod returns an object that can be passedmonitor.getDropResult()Get the return result.
    • CanDrag (props, monitor): This parameter is optional. You can specify whether the current drag-and-drop operation is allowed.
    • IsDragging (props, monitor): Optional. The event that is triggered when you drag it. Note that it cannot be called again in this methodmonitor.isDragging().

    Description of parameters in the method:

    • Props: of the current componentpropsParameters.
    • Monitor: aDragSourceMonitorInstance. It allows you to get current drag information, such as the currently being dragged item and its type, current and initial coordinates and offsets, and whether it has been deleted.
    • Component: is an instance of a component. You can use it to access DOM elements to make position or size measurements, or to call methods defined in the component, or to performsetStateOperation. Sometimes this may not be obtained in the isDragging, canDrag methodscomponentThis parameter because the instance may not be available when they are called.
  • Collect: This parameter is mandatory. It infuses the information required during the drag and drop into props of the component and receives connect and monitor parameters.

    • connect: DragSourceConnectorExamples includedragPreview()dragSource()Two methods, the most common ones aredragSource()This method.
      • DragSource: Returns a function that is passed to the component to be used tosource DOMReact DnD BackendConnect.
      • DragPreview: Returns a function passed to the component to preview as it dragsDOMNodes andReact DnD BackendConnect.
    • monitor: DragSourceMonitorExamples, including the specific method can refer tohere.

DropTarget

DropTarget is a high-level component that can hold a drop component and respond to hover or dropped events.

Basic usage:

import { DropTarget } from 'react-dnd'

class MyComponent {
  / *... * /
}

export default DropTarget(types, spec, collect)(MyComponent)
Copy the code

Parameters:

  • Types: Specifies the drag element type. The value type can be String, symbol, or array. Drop target only accepts drag sources of the same type.

  • Spec: A JS object that defines methods describing how drag targets react to drag events.

    • Drop (props, monitor, Component): This parameter is optional. Called when item is placed on the target element. If this method returns a JS object, thedrag sourceendDragMethod, callmonitor.getDropResult()You can get the return result.
    • Hover (props, monitor, Component): Optional parameter. When the item afterdrop targetIs called when. Can be achieved bymonitor.isOver({ shallow: true })Method to check whether the hover occurs only on the current target or nested.
    • CanDrop (props, monitor): This parameter is optional. This method can be used for detectiondrop targetWhether to accept item.

    Description of parameters in the method:

    • Props: of the current componentpropsParameters.
    • Monitor: aDropTargetMonitorInstance. It allows you to get current drag information, such as the currently being dragged item and its type, current and initial coordinates and offsets, and whether it has been deleted.
    • Component: is an instance of a component. You can use it to access DOM elements to make position or size measurements, or to call methods defined in the component, or to performsetStateOperation. Sometimes this may not be obtained in the isDragging, canDrag methodscomponentThis parameter because the instance may not be available when they are called.
  • Collect: This parameter is mandatory. It infuses the information required during the drag and drop into props of the component and receives connect and monitor parameters.

    • connect: DropTargetConnectorExamples includedropTargetOne way.
      • DropTarget: Returns a function passed to the component that willsource DOMReact DnD BackendConnect.
    • monitor: DropTargetMonitorExamples, including the specific method can refer tohere.

DragDropContext && DragDropContextProvider

Components wrapped with DragSource and DropTarget must be placed inside the DragDropContext or DragDropContextProvider component.

Basic usage:

import Backend from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'

export default function MyReactApp() {
  return (
    <DndProvider backend={Backend}>
      /* your drag-and-drop application */
    </DndProvider>)}Copy the code

Parameters:

  • Backend: Mandatory. HTML5 DnD API is not compatible and is not suitable for mobile terminals. Therefore, IT is better to separate DOM events related to DnD and use Backend as a separate layer, namely Backend. We can define our own Backend according to the conventions provided by React DnD.

The sample

Now that you know the basics of the API, let’s implement the first demo.

Create-react-app: create-react-app: create-react-app: create-react-app: create-react-app

$ create-react-app react-dnd-demo
Copy the code

src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import Container from './Container'
import { DndProvider } from 'react-dnd'
import Backend from 'react-dnd-html5-backend'

function App() {
  return (
    <div className="App">
      <DndProvider backend={Backend}>
        <Container />
      </DndProvider>
    </div>)}const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)
Copy the code

src/Container.js

import React from 'react';
import { DropTarget } from 'react-dnd';
import DraggableBox from './DraggableBox';
import Types from './types'

const styles = {
  width: '500px'.height: '300px'.position: 'relative'.border: '1px solid black',
}

@DropTarget(
  Types.Box,
  {
    drop: (props, monitor, component) = > {
      if(! component) {return;
      }

      const delta = monitor.getDifferenceFromInitialOffset();
      const item = monitor.getItem();
      const left = Math.round(delta.x + item.left);
      const top = Math.round(delta.y + item.top);

      component.moveBox(item.id, left, top);
    },
  },
  (connect, monitor) => ({
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
    canDrop: monitor.canDrop(),
  })
)
class Container extends React.Component {
  state = {
    boxes: {
      a: { top: 20.left: 80.title: 'Drag me around' },
      b: { top: 180.left: 20.title: 'Drag me too' },
    },
  }

  moveBox = (id, left, top) = > {
    const { boxes }  = this.state;
    this.setState({
      boxes: {... boxes, [id]: { ... boxes[id], left, top } } }) } render() {const { isOver, canDrop, connectDropTarget} = this.props;
    const { boxes } = this.state;
    const isActive = isOver && canDrop;

    let backgroundColor = '#ccc';
    // When the component is in the Drag Target area, the background color of the current component changes to darkgreen
    if (isActive) {
      backgroundColor = '# 453467';
    }

    console.log('qqqq'.this.state.boxes)

    return connectDropTarget && connectDropTarget(
      <div style={{ . styles.backgroundColor}} >
        {Object.keys(boxes).map(item => <DraggableBox {. boxes[item]} id={item} />)}
      </div>
    )
  }
}

export default Container;
Copy the code

As you can see, in the drop method, through the monitor. The getDifferenceFromInitialOffset () method to calculate the every time drop, before the current element and drag-and-drop elements position offset, The monitor. GetItem () method gets which element is currently being dragged (which must be returned in the beginDrag method of the Drag Source), calls the moveBox method on the Component to reset the latest position after the drag. To move the elements.

IsOver () and monitor.candrop () are used to pass isOver and canDrop parameters to the props of the component to determine whether the current component is in the drag state. This can be used to set the container’s background color when it is dragged.

One detail to note here is that the position attribute of the Container is set to relative, so that the elements being dragged are positioned relative to the Container.

src/DraggableBox.js

import React from 'react';
import { DragSource } from 'react-dnd';
import Box from './Box';
import Types from './types'

@DragSource(
  Types.Box,
  {
    beginDrag: (props) = > {
      const { id, title, left, top } = props
      return { id, title, left, top }
    }
  },
  (connect, monitor)=> ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging(),
  })
)
class DraggableBox extends React.Component {
  getStyle = (a)= > {
    const { left, top } = this.props;

    const transform = `translate(${left}px, ${top}px)`
    return {
      position: 'absolute',
      transform,
    }
  }

  render() {
    const { connectDragSource } = this.props;
    return connectDragSource(
      <div style={this.getStyle()}><Box {. this.props} / ></div>
    )
  }
}

export default DraggableBox;
Copy the code

The beginDrag method must return an object that was previously retrieved from the drop method for the currently being dragged component. The positon property must be set to Absolute to facilitate positioning relative to the container. Element movement is controlled by the TRANSFORM property of the CSS.

src/Box.js

import React from 'react';

const styles = {
  border: '1px dashed gray'.backgroundColor: 'white'.padding: '0.5 rem 1 rem'.marginRight: '1.5 rem'.marginBottom: '1.5 rem'.cursor: 'move'.display: 'inline-block'
}

class Box extends React.Component {
  render() {
    const { title, left, right } = this.props;
    return (
      <div style={{... styles}}>
        {title}
      </div>)}}export default Box;
Copy the code

conclusion

React DnD: React DnD: React DnD: React DnD: React DnD