DEV Community

roggc
roggc

Posted on

How to implement drag and drop in React

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:

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
}
Enter fullscreen mode Exit fullscreen mode

This is how this component look like on the screen (browser):
Alt Text
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:

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
}
Enter fullscreen mode Exit fullscreen mode

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:

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
}
Enter fullscreen mode Exit fullscreen mode

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. 🍺

Latest comments (1)

Collapse
 
asebaiden profile image
AS-EBaiden

Is there a demo, or a repo where we can reference where withDraggable and the images come from