Do you really understand the drag artifact react DND?

Time:2020-2-13

brief introduction

Recently in researchReactWhen drawing the topology, it involvesHTML5Drag and dropAPI, got itReact DnDThis drag and drop artifact.React DnDIt helps us encapsulate a series of drag and dropAPI, greatly simplifying drag and dropAPIToday, I’d like to introduce you to the following exampleReact DnDUsage.
Do you really understand the drag artifact react DND?

Important concepts

React DndProvides several importantAPIFor our use:

  • DragSource
  • DropTarget
  • DragDropContext && DragDropContextProvider

DragSource

DragSourceIs a high-level component, usingDragSourceComponents wrapped by high-level components can be dragged.

Basic usage:

import { DragSource } from 'react-dnd'

class MyComponent {
  /* ... */
}

export default DragSource(type, spec, collect)(MyComponent)

Parameters:

  • Type: Specifies the type of drag element. The value type can bestringsymbolperhapsfunc, only elements of the same type can bedrop targetRespond.
  • Spec: a JS object, which defines some methods to describedrag sourceHow to respond to drag events.

    • Begindrag (props, monitor, component): required. When the drag starts, the method will be called. The method must return a JS object to describe the dragged element, such as returning a{ id: props.id }Throughmonitor.getItem()Method to get the return result.
    • Enddrag (props, monitor, component): not required. When the drag stops, this method will be called through themonitor.didDrop()Can judgedrag sourceHas it beendrop targetFinished processing. If indrop targetOfdropMethod, where you can use themonitor.getDropResult()Get the returned result.
    • Candrag (props, monitor): optional parameter. You can specify whether the current drag operation is allowed.
    • Isdragging (props, monitor): optional parameter. Event triggered when dragging. Note that this method can no longer be calledmonitor.isDragging()
Parameter interpretation in method:
-Props: the 'props' parameter of the current component.
-Monitor: an instance of 'dragsourcemonitor'. It can obtain the current drag information, such as the current dragged item and its type, current and initial coordinates and offsets, and whether it has been deleted.
-Component: is an instance of a component. It can be used to access DOM elements for position or size measurement, call methods defined in components, or perform 'setstate' operation. Sometimes the parameter 'component' may not be obtained in isdragging and candrag methods, because the instance may not be available when they are called.
  • Collect: required item to inject the information needed in the dragging process into theprops, receive two parametersconnectandmonitor

    • connect: DragSourceConnectorExamples of, includingdragPreview()anddragSource()Two methods are commonly useddragSource()This method.

      • Dragsource: returns a function passed to the component tosource DOMandReact DnD BackendConnect.
      • Dragpreview: returns a function passed to the component to previewDOMNode sumReact DnD BackendConnect.
    • monitor: DragSourceMonitorThe specific methods contained in the example can refer to here.

DropTarget

DropTargetIs a high-level component, which isDropTargetPackage components can place drag components, andhoverperhapsdroppedEvent response.

Basic usage:

import { DropTarget } from 'react-dnd'

class MyComponent {
  /* ... */
}

export default DropTarget(types, spec, collect)(MyComponent)

Parameters:

  • Types: Specifies the type of the drag element. The value type can bestringsymbolperhapsarraydrop targetOnly those with the same type are accepteddrag source
  • Spec: a JS object, which defines some methods and describes the response of the drag and drop target to the drag and drop event.

    • Drop (props, monitor, component): optional parameter. Called when an item is placed on a target element. If this method returns a JS object, thedrag sourceOfendDragMethod, callmonitor.getDropResult()You can get the returned results.
    • Hover (props, monitor, component): optional parameter. When item passesdrop targetIs called when. Can passmonitor.isOver({ shallow: true })Method to check whether hover occurs only on the current target or nested.
    • Candrop (props, monitor): optional parameter. This method can be used to detectdrop targetAccept item or not.
Parameter interpretation in method:
-Props: the 'props' parameter of the current component.
-Monitor: an instance of 'droptargetmonitor'. It can obtain the current drag information, such as the current dragged item and its type, current and initial coordinates and offsets, and whether it has been deleted.
-Component: is an instance of a component. It can be used to access DOM elements for position or size measurement, call methods defined in components, or perform 'setstate' operation. Sometimes the parameter 'component' may not be obtained in isdragging and candrag methods, because the instance may not be available when they are called.
  • Collect: required item to inject the information needed in the dragging process into theprops, receive two parametersconnectandmonitor

    • connect: DropTargetConnectorExamples of, includingdropTargetOne way.

      • Droptarget: returns a function passed to the component to use tosource DOMandReact DnD BackendConnect.
    • monitor: DropTargetMonitorThe specific methods contained in the example can refer to here.

DragDropContext && DragDropContextProvider

UseDragSourceandDropTargetPackage components must be placed inDragDropContextperhapsDragDropContextProviderInside the 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>
  )
}

parameter

  • Backend: required. HTML5 DND API compatibility is not good, and it is not applicable to the mobile end, so we simply pull out the specific DOM events related to DND, as a single layer, namely backend. We can define our own backend according to the protocol provided by react DND.

Example

Now that we understand the basic use of the above API, let’s implement the demo at the beginning.

This example is developed based on create react app. Create our demo project through the CLI tool of create react app:

$ create-react-app react-dnd-demo

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)

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

As you can see, indropMethod, throughmonitor.getDifferenceFromInitialOffset()Method to calculate each timedropThe offset between the current element and the position of the element before dragging,monitor.getItem()Method to get which element is currently dragged (must be in thedrag sourceOfbeginDragMethod), callingcomponentUppermoveBoxMethod resets the latest position after the drag to move the element.

collectOfconnectMethod passedmonitor.isOver()andmonitor.canDrop()Method willisOverandcanDropParameters passed to thepropsTo determine whether the current component is in the dragging state, this can be used to set the background color of the container when dragging.

Here’s a detail to note: the container’spositionProperty is set torelative, so that the dragged element is 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 = () => {
    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;

beginDragMethod must return an object. Previously, the information of the currently dragged component was obtained in the drop method. The position property must be set to absolute to facilitate positioning relative to the container. Elements are moved bycssOftransformProperty.

src/Box.js

import React from 'react';

const styles = {
  border: '1px dashed gray',
  backgroundColor: 'white',
  padding: '0.5rem 1rem',
  marginRight: '1.5rem',
  marginBottom: '1.5rem',
  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;

summary

aboutReact DnDHere is just a basic introduction. For more examples, you can refer to the official website. The code of this example can be found here.

Do you really understand the drag artifact react DND?

Recommended Today

[reading notes] calculation advertising (Part 3)

By logm This article was originally published at https://segmentfault.com/u/logm/articles and is not allowed to be reproduced~ If the mathematical formula in the article cannot be displayed correctly, please refer to: Tips for displaying the mathematical formula correctly This article isComputing advertising (Second Edition)Reading notes. This part introduces the key technology of online advertising, which is […]