DEV Community

Cover image for Drag n Drop Walkthrough in React
Vinay P
Vinay P

Posted on

Drag n Drop Walkthrough in React

  • 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.
  2. Store the data of the element in an attribute.
  3. 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>
    );
};
Enter fullscreen mode Exit fullscreen mode
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.
  2. 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>
Enter fullscreen mode Exit fullscreen mode

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);
    }
};
Enter fullscreen mode Exit fullscreen mode
  • handleOnDragOver
const handleOnDragOver = (ev) => {
    ev.stopPropagation();
    ev.preventDefault();
};
Enter fullscreen mode Exit fullscreen mode
  • handleOnDragEnter
const handleOnDragEnter = (ev) => {
    //  Modify the styles of the container if requried.
};
Enter fullscreen mode Exit fullscreen mode
  • handleOnDragEnter
const updateCardStatus = (itemData, droppedState) => {
    const newCardItems = cardItems.map((eachCard) => {
        if (eachCard.id === itemData.id) {
            return { ...eachCard, state: droppedState };
        }
        return eachCard;
    });
    setCardItems(newCardItems);
};
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)