React Drag N' Drop
Tags: react, javascript, html, css
Published: 2020-11-19 22:49:04

# Introduction
If UI design was like a full course Thanksgiving meal, "Drag N' Drop" would easily qualify as bread and butter. Or the turkey. Or even the ham. ORR... **ahem**... you get the point! :wink: It's good stuff. This is an ideal feature to have on our websites. It helps users feel like they are truly in control of their experience while using our applications.

**NOTE: This is going to be a 7 minute read, and it might take you a bit longer to follow along. I totally understand and sympathize with you not wanting to waste your time! I get wanting to follow the right tutorial. :heart: So, if you want to see this live before continuing, [Click here... *on a desktop computer*... This is not mobile friendly.](https://react-drag-drop-five.vercel.app/)**

So, without further ado, let's get right down to it.

### Important Info
There's a few concepts I'd like to cover here. If you're already familiar with web APIs, here's a couple sources for a quick recap on `dataTransfer`, `dataTransfer.getData()` and `dataTransfer.setData()`:
[dataTransfer](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer)
[dataTransfer.getData()](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/getData)
[dataTranser.setData()](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setData)

These concepts were personally a tough one for me to understand, so not to worry -- I'll cover what exactly is happening in sufficient detail within this blog.

### Setup
Let's start from scratch. Create a React application by typing `npx create-react-app your-choice-appname` into your terminal and pressing enter, with "your-choice-appname" being literally whatever you want to name this project. 

Once this is complete, let's do some cleanup. Remove `App.test.js` and let's rename `index.css` to `main.css`. Just because we can. :thumbsup: 


Next, you'll want to make sure you're importing your `main.css` inside of your `index.js`, like so:

```
import React from 'react';
import ReactDOM from 'react-dom';
import './main.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

...
```

Perfect!

### Creating Board and Card Components
We are going to be following the separation of concerns rule, so let's create a folder in our source folder called "components" -- `src/components`.

Inside of this folder, create two files `Card.jsx` and `Board.jsx`. 

These will be functional components that will accept `props` as arguments between each other. This will be necessary for a transfer of data back and forth.

**NOTE: This is where things begin to get slightly confusing. To understand what happens next will require understanding what is happening in both `Card.jsx` and `Board.jsx` simultaneously. I'll be providing a thorough explanation, so bare with me. Once it clicks, you'll have what I like to call an "AHA! moment."**

## Board.jsx
Let's begin with a Board component skeleton. This is what we will start off with:

```
import React from 'react';

export default function Board(props) {

    return (
        <div>
            
        </div>
    )
}
```

Before we jump into the applied logic of this component, we need to set some properties for our `div`. We need an `id` and a `className` assigned to it, and this is where `props` comes in. Let's change it to reflect the dynamic operation we want it to perform.

```
import React from 'react';

export default function Board(props) {

    return (
        <div 
            id={props.id} 
            className={props.className}
        >
        
        </div>
    )
}
```

The reason for this is because later on we will be using this board component in a manner such as this:
`<Board id="1" className="board"></Board>`
As you can see, our props will be "id" and "className".

#### `drop`
Now we can begin adding in our functions. We want to handle two React events on our boards. They are `onDrop` (for when we drop a `card` in this `board`) and `onDragOver` (to handle tracking the data of a `card` as it is being dragged by your cursor into a `board`). Let's apply these events to our `div`. 

**NOTE: These events will be triggering from functions we haven't created yet, but we will be creating them next.**

```
export default function Board(props) {

    return (
        <div 
            id={props.id} 
            className={props.className}
            onDrop={drop}
            onDragOver={dragOver}
        >
        
        </div>
    )
}
```

Alright, now for the fun part! We will be creating a function called `drop` and place this above our `return()`:

```
export default function Board(props) {
     const drop = e => {
        const card_id = e.dataTransfer.getData('card_id');
        const card = document.getElementById(card_id);

        e.target.appendChild(card);
    }

    return (
        <div 
            id={props.id} 
            className={props.className}
            onDrop={drop}
            onDragOver={dragOver}
        >
        
        </div>
    )
}
```

"*Whoa, wait! What's going on here MATT?*"

I'm glad you asked! Let's start with the first two declarations in our `drop` function. 

`const card_id = e.dataTransfer.getData('card_id')` will be responsible for acquiring the data from the `card` we will be dragging into the `board` later on. We are setting a declaration of "card_id" set to this `dataTransfer`, which will be coming directly from our cursor when we `drop` a `card`. (Sorry if I'm being redundant/repeating myself. I feel if you "get the point", then I'm explaining this well. :wink:)

Next, we will be setting another declaration of "card" which is being set towards grabbing the `card` element ID in the DOM so that it can be dropped into the `board`.

Finally, we are using `e.target.appendChild(card)` to add our `card` to `e.target` (`e.target` being the current `board` the `card` is being dropped into.).

#### dragOver
This one is short and sweet. All we want to do is create a `dragOver` function which takes `e` as an argument for `event` and preventing the default behavior of our `onDragOver` React event. Basically, we want to prevent `onDragOver` from snapping our `card` back to the original position it was dragged from on the `board` it came from. This event must be started but must not be completed in order for our `onDrop` event to fire.

```
 const dragOver = e => {
        e.preventDefault();
 }
```

To wrap this up, we want all of our cards to be displayed on our page. To do this, we simply add `{ props.children }` between our `div`.

Your finished `Board.jsx` component should look like this:

```
import React from 'react';

export default function Board(props) {
    const drop = e => {
        const card_id = e.dataTransfer.getData('card_id');
        const card = document.getElementById(card_id);
        
        e.target.appendChild(card);
    }

    const dragOver = e => {
        e.preventDefault();
    }

    return (
        <div 
            id={props.id} 
            className={props.className}
            onDrop={drop} 
            onDragOver={dragOver}
        >
            { props.children }
        </div>
    )
}
```

## Card.jsx
Time for our `Card.jsx` component! We are going to start off similarly to the way we set up our `Board.jsx`:

```
import React from 'react';

export default function Card(props) {
    return (
        <div>

        </div>
    )
}
```

Next, let's set some properties in our `div`. In addition to an `id` and `className` like we have in our `Board.jsx` component, we want to apply a special property to our cards called `draggable`. This property will need to be set to `true` in order for our cards to be, well... you guessed it -- draggable. 

```
import React from 'react';

export default function Card(props) {
    return (
        <div
            id={props.id}
            draggable={props.draggable}
            className={props.className}
        >
            
        </div>
    )
}
```

As you might've concluded, we will be using this component similar to the way we use `<Board></Board>` like so:

```
<Card id="1" className="card" draggable="true">
    <p>Card one</p>
</Card>
```

Now we can begin adding in functions `dragStart` (which will handle moving the card data into your cursor) and `dragOver` (which will be used to prevent cards from being dropped into other cards). Both will be executed by React events `onDragStart` and `onDragOver`. 

```
import React from 'react';

export default function Card(props) {
    return (
        <div
            id={props.id}
            draggable={props.draggable}
            className={props.className}
            onDragStart={dragStart}
            onDragOver={dragOver}
        >
            
        </div>
    )
}
```

#### dragStart
Good stuff! Now let's add in those functions. Right above our `return()`, we can start with our `dragStart` function:

```
const dragStart = e => {
    const target = e.target;
    e.dataTransfer.setData('card_id', target.id)
}
```

We are making a declaration of `target` which will be assigned to `e.target` (`e.target` being the `card` in question that we will be dragging). Next, we are introduced to another function of the HTML Drag and Drop API: `e.dataTransfer.setData('card_id', target.id)`. What is happening here is we are setting the data in our cursor referenced as `card_id` and assigning the ID of the card we are dragging (`target.id`) to this reference.

**DING DING... :bulb: Remember `e.dataTransfer.getData('card_id')` in our `Board.jsx` component? The `card` data is SET in the `Card.jsx` component, and the `Board.jsx` GETS that data... See? I told you this would all click. :wink:**

#### dragOver
Our final function... `dragOver`. This one is short and straight forward. All we need to do for this function is to apply `stopPropagation` to the event. The purpose of this function is to prevent cards from being able to be dropped into other cards. Otherwise, things can quickly become messy for our users!

```
const dragOver = e => {
    e.stopPropagation();
}
```

Finally, don't forget to add `{ props.children }` to the `div` just like we did for `Board.jsx`.

There we have it! We are all set to apply these components. 

# Showtime
Go into your `App.js` and import `Card.jsx` and `Board.jsx` from `src/component`. Finally, we will place two boards and two cards in each board rendered to our web page. Here's what your `App.js` should look like:

```
import React, { Component } from 'react';
import Board from './components/Board.js';
import Card from './components/Card.js';

export default class App extends Component {  
  
  render() {
    return (
      <div className="App">
        <main className="flexbox">
          <Board id="board-1" className="board">
            <Card id="1" className="card" draggable="true">
              <p>Card one</p>
            </Card>
            <Card id="2" className="card" draggable="true">
              <p>Card two</p>
            </Card>
          </Board>

          <Board id="board-2" className="board">
            <Card id="3" className="card" draggable="true">
              <p>Card three</p>
            </Card>
            <Card id="4" className="card" draggable="true">
              <p>Card four</p>
            </Card>
          </Board>
        </main>
      </div>
    )
  }
}
```

There's one more thing you'll want to do... Apply some styling in your `main.css` so you can easily see your components. This should suffice for now:

```
* {
    margin: 0; padding: 0; box-sizing: border-box;
}

body {
    background-color: #f3f3f3;
}

.flexbox {
    display: flex;
    justify-content: space-between;
    width: 100%;
    max-width: 786px;
    height: 100vh;

    overflow: hidden;

    margin: 0 auto;
    padding: 15px;
}

.flexbox .board {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 300px;
    background-color: #313131;
    padding: 15px;
}

.flexbox .board .card {
    padding: 15px 25px;
    background-color: #f3f3f3;

    cursor: pointer;
    margin-bottom: 15px;
}
```

Crank up that `npm start` and play around with the cards! 

# Conclusion
As developers, we often place an awful stigma on processes that seem far more complicated than they actually are. The Drag N' Drop function, to me, sounded like it was going to be way worse than this method. Although there is way more you could do to make the experience more robust, hopefully this will set the rest of you off with a fair hand. :) Happy coding, y'all!