How to implement drag and drop in React
Tags: react
Published: 2019-11-10 19:32:34

In this post, I will show you a simple implementation of drag and drop with React. It uses HTML API for drag and drop.
We will have a component named `Card` and a hoc (high order component) named `withDraggable` which will implement the drag and drop logic.
Let's begin by looking at our `Card` component:
```javascript
import React from 'react'
import styled from 'styled-components'

export const Card=
({name,img,...rest})=>
{
  const Card=styled.div`
  background-color: antiquewhite;
  margin: 5px 5px;
  padding: 40px;
  border-radius: 8px;
  width: 186px;
  height:250px;
  text-align: center;
  box-shadow: 0 16px 32px -16px #B0BEC5;
  border: 4px groove rgb(166,55,198);
  cursor:pointer;
  float:left;
  img {
    width: 100%;
    height:100%;
  }
  h2 {
    margin-top: 8px;
    font-weight: 900;
    color: #4CAF50;
  }
  `
  const el=
  <Card {...rest}>
    <img
      src={img}
      alt="Profile image" draggable='false'/>
    <h2>{name}</h2>
  </Card>
  return el
}
```
This is how this component look like on the screen (browser):
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/qk7z6bccpi6h1b42dq1u.png)
So we have this component `Card` per se it is not draggable. Now let's see drag and drop implementation, that's it, let's see `withDraggable` hoc:
```javascript
import React from 'react'
import styled from 'styled-components'

export default
C=>({empty,...props})=>
{
  const onDragOver=e=>
  {
    e.preventDefault()
    const element=e.currentTarget
    element.style.backgroundColor='purple'
  }
  const onDragLeave=e=>
  {
    const element=e.currentTarget
    element.style.backgroundColor='white'
  }
  const onDrop=e=>
  {
    const insertAfter=(newNode,referenceNode)=> {
      referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)
    }
    const dropZone=e.currentTarget
    dropZone.style.backgroundColor='white'
    const id=e.dataTransfer.getData('text')
    const draggableElement=document.getElementById(id)
    insertAfter(draggableElement.parentNode,dropZone.parentNode)
    e.dataTransfer.clearData()
  }
  const onDropLeft=e=>
  {
    const insertBefore=(newNode,referenceNode)=> {
      referenceNode.parentNode.insertBefore(newNode,referenceNode)
    }
    const dropZone=e.currentTarget
    dropZone.style.backgroundColor='white'
    const id=e.dataTransfer.getData('text')
    const draggableElement=document.getElementById(id)
    insertBefore(draggableElement.parentNode,dropZone.parentNode)
    e.dataTransfer.clearData()
  }
  const onDragStart=
  e=>
  {
    const element=e.currentTarget
    e.dataTransfer.setData('text',e.target.id)
  }
  const DroppableCardZone=styled.div`
  width:10px;
  margin: 10px 0px;
  border-radius:3px;
  `
  const Container=styled.div`
  float:left;
  display:flex;
  `
  const el=
  <Container>
    <DroppableCardZone onDragOver={onDragOver} onDrop={onDropLeft} onDragLeave={onDragLeave}/>
    <C {...props} draggable='true' onDragStart={onDragStart}/>
    <DroppableCardZone onDragOver={onDragOver} onDrop={onDrop} onDragLeave={onDragLeave}/>
  </Container>
  return el
}
```
As you can see in this file, we first make `C` component draggable with the property `draggable='true'`. All this is HTML API.
Then we pay attention to  `onDragOver` event handler. The first line of code which is `e.preventDefault()` it is meant to allow for a dropping area (the default it is not to allow it). Then we change style so people (users) can see where to drop the dragged element. 
Then we look at `onDrop` event handler. There are two handlers for this event, `onDrop` and `onDropLeft`. The first one is for dropping to the right and the second one for dropping to the left. We will comment on only one of them (the other one it's almost the same). What we do in this event handler it is first to change back the style of the dropping zone which was changed on the `onDragOver` event handler (`dropZone.style.backgroundColor='white'`). Then we get the `id` of the dragged element (`const id=e.dataTransfer.getData('text')`). `dataTransfer` is part of the HTML API. Then we get the element itself (`const draggableElement=document.getElementById(id)`). Finally we insert the `DraggableCard` after the droppable zone (`insertAfter(draggableElement.parentNode,dropZone.parentNode)`).
It only rests us to see the `onDragStart` event handler. What we do on this event handler is this: `e.dataTransfer.setData('text',e.target.id)`, that's it, we use the HTML API for drag and drop to set the `id` of the dragged element.
Let's look at the `App` component:
```javascript
import React from 'react'
import * as S from '../../styled/styled'
import withDraggable from '../withDraggable/withDraggable'
import {Card} from '../card/card'
import imgOne from '../../images/one.png'
import imgTwo from '../../images/two.png'
import imgThree from '../../images/three.png'
import imgFour from '../../images/four.png'
import imgFive from '../../images/five.png'

export const App=
()=>
{
  const DraggableCard=withDraggable(Card)
  const el=
  <S.Div>
    <DraggableCard
      img={imgOne}
      name={"roger"}
      id='card-1'/>
    <DraggableCard
      img={imgTwo}
      name={"gomez"}
      id='card-2'/>
    <DraggableCard
      img={imgThree}
      name={"alejandro"}
      id='card-3'/>
    <DraggableCard
      img={imgFour}
      name={"gonzalez"}
      id='card-4'/>
    <DraggableCard
      img={imgFive}
      name={"alberto"}
      id='card-5'/>
  </S.Div>
  return el
}
```
So that's it. We have developed a hoc (high order component) which implements the logic for drag and drop in React, and applied to a component `Card` we had. 🍺