Drag n Drop Walkthrough in React
Vinay P
Posted on December 20, 2022
- 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
- Make the card draggable by setting
draggable
attribute on the card to true. - Store the data of the element in an attribute.
- Set an
onDragStart
listener on the card and once it is started, take the card data (from the attribute) and set it todataTransfer
object of the event usingsetData
function ofdataTransfer
.
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
- Set an
onDrop
listener on the element on which the drop happens.- Pass a function that will take the card data from
dataTransfer
object usinggetData
function ofdataTransfer
. - Check the initial state of the card data and the newly dragged state and make appropriate changes and set new data.
- Pass a function that will take the card data from
- By defult, browser prevents the
drop
events on elements. SoonDragOver
, prevent the default behavior usingpreventDefault()
andstopPropagation()
.
<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
💖 💪 🙅 🚩
Vinay P
Posted on December 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.