Drag and Drop starter project with ReactJS - Part 1
Tags: react, javascript, starterproject, draganddrop
Published: 2021-04-29 10:20:05

&nbsp;
## _Hello fellow developers,_
&nbsp;

This will be about creating a drag and drop starter project in ReactJS. [ReactJS](https://reactjs.org/) is an open-source, JavaScript framework for building interactive user interfaces. We would be developing a Single Page Application (SPA) that would enable us to 
&nbsp;
1. Drag and drop components
2. Rearrange dropped components
3. Remove dropped components
&nbsp;
We would be creating our own components and using some of the flexbox tricks I explained in my [previous blog](https://dev.to/vedant1202/styling-concepts-every-frontend-guy-must-know-about-2nmd). 
&nbsp;
By the end of this series, we would have the following app
[Drag and Drop Starter demo](http://Vedant1202.github.io/Drag-and-Drop-Starter-)

&nbsp;
## 1. First we setup a project with [Create React App](https://github.com/facebook/create-react-app)
&nbsp;
_Make sure you have Node (>v12) installed, if not install from [here](https://nodejs.org/en/)_
&nbsp;
Open a terminal and run
```bash
npx create-react-app app ## replace app by any name you like
```
&nbsp;
React will build the app for you. Once its done, go to the directory and run the app
```bash
cd app ## or whatever you named it
npm start
```
&nbsp;
Go to the browser at `http://localhost:3000` and you should see the following page
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j3h0t4q2ykrezyee3eap.png) 
&nbsp;
If you can see that, our setup is good, onto the next step

&nbsp;
## 2. Add in static UI elements
&nbsp;
First, we clear everything in the `src\App.css` and edit the App component in `src\App.js` as follows. Note that we are also converting the Functional component to Class component because we would be using `state` to handle drag and drop features. Learn more about [class and functional components here](https://reactjs.org/docs/components-and-props.html).
&nbsp;
```jsx
// App.js
...

    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
...
```
&nbsp;
To 
&nbsp;
```jsx
//App.js
...
    <div className="App">
      
    </div>
...
```
&nbsp;
And remove the line `import logo from './logo.svg';` from it.
&nbsp;
Now add our UI Components to the App Component
&nbsp;
```jsx
//App.js
import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() { 
    return (
      <div className="App">
        <div className="components-list">
          <div className="component blue">
            Blue Component
          </div>
          <div className="component green">
            Green Component
          </div>
          <div className="component purple">
            Purple Component
          </div>
        </div>
        <div className="drop-area">
          Drop Area
        </div>
      </div>
    );
  }
}
 
export default App;

```
&nbsp;
And `App.css` as 
```css
/* App.css */
.App {
  display: flex;
  flex-wrap: nowrap;
  padding: 10px 10px 0 10px;
}

.components-list {
  width: 18vw;
  margin-right: 2vw;
  padding: 4vh 2vw 0 2vw;
  height: 95vh;
}

.drop-area {
  width: 80vw;
  height: 95vh;
  border: 1px black dashed;
  border-radius: 1vw;
  font-size: 64px;
  text-align: center;
  line-height: 95vh;
  color: gray;
  user-select: none;
}

.component {
  cursor: pointer;
  width: 12vw;
  text-align: center;
  font-size: 20px;
  line-height: 10vh;
  background: white;
}

.components-list .component {
  margin-bottom: 4vh;
}

.blue {
  color: blue;
  border: 2px solid blue;
  border-radius: 10px;
}

.green {
  color: green;
  border: 2px solid green;
  border-radius: 10px;
}

.purple {
  color: purple;
  border: 2px solid purple;
  border-radius: 10px;
}
```
&nbsp;
This should result in the following screen
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qk2sghhnt58q1hs8ydu3.png)
 

 &nbsp;
## Making the drag and drop feature
&nbsp;
Now, we make our component divs draggable. To do this, we set the draggable attribute to `true`.
&nbsp;
```jsx
//App.js
...
          <div className="component blue" draggable={true}>
            Blue Component
          </div>
          <div className="component green" draggable={true}>
            Green Component
          </div>
          <div className="component purple" draggable={true}>
            Purple Component
          </div>
...
```
&nbsp;
Alright, you should be able to drag your div now
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cq20j01yh8j4r6njn65s.png)
 &nbsp;

Next, we have to handle the `onDragStart` event. The plan is to define three categories, which would be used to identify the type of div being dragged. For this, we can choose three simple names ie, 'blue' for the blue component, 'green' for the green component, and 'purple' for the purple component. We store this data in the `dataTransfer`[learn more](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) attribute of the `drag` event. 
&nbsp;
Create a class method in the `App` component as follows,
&nbsp;
```jsx
//App.js
...
  dragStart = (ev, type) => {
    ev.dataTransfer.setData("type", type);
  }
...
```
&nbsp;
And assign that method to the `onDragStart` attribute of the divs along with their respective "types"(, or categories)
&nbsp;
```jsx
//App.js
...
            <div className="component blue" draggable={true} onDragStart={(ev) => { this.dragStart(ev, 'blue') }}>
            Blue Component
          </div>
          <div className="component green" draggable={true} onDragStart={(ev) => { this.dragStart(ev, 'green') }}>
            Green Component
          </div>
          <div className="component purple" draggable={true} onDragStart={(ev) => { this.dragStart(ev, 'purple') }}>
            Purple Component
          </div>
...
```
&nbsp;
Now, we have to retrieve this data at the drop event on the Drop Area. For this, we create a class method that would work as the handler to handle the same. 
&nbsp;
```jsx
//App.js
...
  drop = (ev) => {
    ev.preventDefault();
    const type = ev.dataTransfer.getData("type");
    console.log(type);
  }
...
```
&nbsp;
We then assign this function to the `onDrop` attribute of the Drop Area
&nbsp;
```jsx
//App.js
...
        <div className="drop-area" onDrop={this.drop} >
          Drop Area
        </div>
...
```
&nbsp;
When you test it, you should see an output on the developer tools of your browser when you drop the div on the drop area
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p74i1w5uqwqgq6jugrvh.png)
&nbsp;
Okay, so everything looks good so far, but we've to still make our app 'memorize' the dropped components and their position. So we create two properties in the App component's state defined in the constructor. These properties are `elementData` (used to hold data of dropped elements) and `nextElementId` (to store a unique key for each dropped element). 
&nbsp;
```jsx
//App.js
...
  constructor(props) {
    super(props);
    this.state = {
      nextElementId: 0,
      elementData: {},
    };
  }
...
```
&nbsp;
And we update the state when any new div is dropped in the Drop Area. So we update the `drop` class method as follows,
&nbsp;
```jsx
//App.js
...
  drop = (ev) => {
    ev.preventDefault();
    const type = ev.dataTransfer.getData("type");
    const { elementData, nextElementId } = this.state;
    const newElementData = {
      type,
      left: ev.clientX,
      top: ev.clientY,
    }

    elementData[nextElementId] = newElementData;
    ev.dataTransfer.clearData();

    this.setState({
      elementData,
      nextElementId: nextElementId + 1, // Increment it for next element
    });
  }
...
```

&nbsp;
Since we're updating the state, we also need to retrieve this data and render the respective elements in the DOM. To do this, we create a new file in the `src` directory named `utility.js`. In this file, we add the following function, and export it
```jsx
//utility.js
const getElementList = (elementData) => {
  const elements = [];

  Object.keys(elementData).forEach(key => {
    let { type, left, top } = elementData[key];

    switch (type) {
      case 'blue':
        elements.push(
          <div
            className="component blue"
            key={key}
            style={{
              position: 'absolute',
              left: left + 'px',
              top: top + 'px',
              zIndex: 100,
            }}
          >
            Blue Component
        </div>
        );
        break;

      case 'green':
        elements.push(
          <div
            className="component green"
            key={key}
            style={{
              position: 'absolute',
              left: left + 'px',
              top: top + 'px',
            }}
          >
            Green Component
        </div>
        );
        break;

      case 'purple':
        elements.push(
          <div
            className="component purple"
            key={key}
            style={{
              position: 'absolute',
              left: left + 'px',
              top: top + 'px',
            }}
          >
            Purple Component
        </div>
        );
        break;

      default:
        break;
    }
  });

  return elements;
}

export { getElementList };
```
&nbsp;
What this function does is it takes an object containing data of dropped elements, and returns an array consisting of HTML elements with their data. This element can be used to render the components in the `render` method of the App component.
&nbsp;
First import the function in App.js
```jsx
//App.js
...
import { getElementList } from './utility';
...
```
&nbsp;
Then, we call the `getElementList` in the render function. We pass the state data as a parameter to this function, and store the returned array in a variable called `elements`. We then render this `elements` array in the DOM inside the Drop Area div.
```jsx
//App.js
...
  render() { 

    const { elementData } = this.state;
    const elements = [ ...getElementList(elementData) ];

    return (
      <div className="App">
        <div className="components-list">
          <div className="component blue" draggable={true} onDragStart={(ev) => { this.dragStart(ev, 'blue') }}>
            Blue Component
          </div>
          <div className="component green" draggable={true} onDragStart={(ev) => { this.dragStart(ev, 'green') }}>
            Green Component
          </div>
          <div className="component purple" draggable={true} onDragStart={(ev) => { this.dragStart(ev, 'purple') }}>
            Purple Component
          </div>
        </div>
        <div className="drop-area" onDrop={this.drop} onDragOver={this.dragOver}>
          Drop Area
          { elements }
        </div>
      </div>
    );
  }
...
```
&nbsp;
When you open the app on the browser, you should be able to drag and drop components on the Drop Area
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7jyqx002x8mbmswepye7.png)

&nbsp;
Good going, we now have built a basic drag and drop feature using nothing but React. Since, this blog is getting too long, I will make it into a series.
 &nbsp;
In part two, we will 
1. Make the components re-arrangeable
2. Make the components delete-able

&nbsp;
Alright, thank you for being with me this far, 
&nbsp;
_Peace and stay safe ✌️_
&nbsp;
You can follow me on my [Dev.to @vedant1202] 
and on my [Github@Vedant1202](https://github.com/Vedant1202)
&nbsp;
_[Link to the Second Part](https://dev.to/vedant1202/drag-and-drop-starter-project-with-reactjs-part-2-91c)_

&nbsp;
### Footnotes

1. Cover image taken from Unsplash [@cleovermij](https://unsplash.com/photos/dFwtwXRQ2yQ?utm_source=unsplash&utm_medium=referral&utm_content=creditShareLink)
2. References from [Mozilla Developer Network Web Documentation](https://developer.mozilla.org/en-US/)