Drag n Drop Walkthrough in React
Tags: react, dragndrop, javascript, webdev
Published: 2022-12-20 11:49:15

- This project demonstrates the concepts to implement a drag and drop functionality.
- It is a simple board with two columns and the cards can be to and from both the columns.

---

### Prerequisites:
- React project setup - `npx create-react-app app_name`

---

### Elements:
- `Card.js` - the item that is being dragged from one column to another. Element with green background.
- `Board.js` - contains the states on which the card is being dropped. Element with blue-ish background

---

### Steps:
##### In `Card.js`

1. Make the card draggable by setting `draggable` attribute on the card to true.
1. Store the data of the element in an attribute.
1. Set an `onDragStart` listener on the card and once it is started, take the card data (from the attribute) and set it to `dataTransfer` object of the event using `setData` function of `dataTransfer`.


```
const Card = ({ cardData = {} }) => {
	const [cardIsBeingDragged, setCardIsBeingDragged] = useState(false);

	const handleOnDragStart = (ev) => {
		let draggedItemData = ev.target.getAttribute("data_card_json") || "{}";
		ev.dataTransfer.setData("draggedItemData", draggedItemData);
		setCardIsBeingDragged(true); // For styling purpose.
	}

	const handleOnDragEnd = () => {
		setCardIsBeingDragged(false); // For styling purpose.
		// console.log("drag stopped");
	}

	return (
		<div
			className={`${styles.card} ${cardIsBeingDragged ? styles.reducedOpacity : ""}`}
			data_card_json={JSON.stringify(cardData)}
			draggable
			onDragStart={handleOnDragStart}
			onDragEnd={handleOnDragEnd}
		>
			<h3>{cardData.name}</h3>
			<h3>State - {cardData.state}</h3>
		</div>
	);
};
```

##### In `Board.js`

1. Set an `onDrop` listener on the element on which the drop happens.
    - Pass a function that will take the card data from `dataTransfer` object using `getData` function of `dataTransfer`.
    - Check the initial state of the card data and the newly dragged state and make appropriate changes and set new data.
1. By defult, browser prevents the `drop` events on elements. So `onDragOver`, prevent the default behavior using `preventDefault()` and `stopPropagation()`.

```
<div className={`${styles.columns}`}>
	<div className={`dropTarget`} data_state="A" onDrop={handleOnDrop} onDragOver={handleOnDragOver} onDragEnter={handleOnDragEnter}>
        {cardItems.map((card, index) => {
		    if (card.state === "A") {
		            return <Card key={index} cardData={card} />;
		    }
		    return null;
	    })}
	</div>
	<div className={`${styles.state} dropTarget`} data_state="B" onDrop={handleOnDrop} onDragOver={handleOnDragOver} onDragEnter={handleOnDragEnter}>
        {cardItems.map((card, index) => {
		    if (card.state === "B") {
			    return <Card key={index} cardData={card} />;
		    }
		    return null;
	    })}	
	</div>
</div>
```
#### Event handlers:
- `handleOnDrop`
```
const handleOnDrop = (ev) => {
	const dropTarget = ev.target.closest(".dropTarget");
	const droppedState = dropTarget.getAttribute("data_state");
	const draggedItemData = JSON.parse(ev.dataTransfer.getData("draggedItemData"));
	const currentItemState = draggedItemData.state;

	if (droppedState !== null && currentItemState !== droppedState) {
	    //  Update the state of the dragged item if its dropped on a different column.
		updateCardStatus(draggedItemData, droppedState);
	}
};
```

- `handleOnDragOver`
```
const handleOnDragOver = (ev) => {
	ev.stopPropagation();
	ev.preventDefault();
};
```

- `handleOnDragEnter`
```
const handleOnDragEnter = (ev) => {
    //	Modify the styles of the container if requried.
};
```

- `handleOnDragEnter`
```
const updateCardStatus = (itemData, droppedState) => {
	const newCardItems = cardItems.map((eachCard) => {
		if (eachCard.id === itemData.id) {
			return { ...eachCard, state: droppedState };
		}
		return eachCard;
	});
	setCardItems(newCardItems);
};
```
---
## Summary
- Make an card draggable.
- Once dragged, store the data about the card in `dataTransfer` object.
- Once dropped on the target, check if the card is dropped on a new state (column) or not.
- If it is new state (column), update the card's data in the state, which renders the UI with newly set data.

### Repo link: [Github](https://github.com/aydotvin/react/tree/main/drag_n_drop)