Drag and Drop with React (with no libraries) part 2

Intro

In this tutorial I will show how to make a Drag and Drop component from scratch (no libraries) using React.

I already made a post on Drag and Drop in React, so why make a new one?

Well, many of us learn new things and think we have a…


This content originally appeared on DEV Community and was authored by Aaron Wolf

Intro

In this tutorial I will show how to make a Drag and Drop component from scratch (no libraries) using React.

I already made a post on Drag and Drop in React, so why make a new one?

Well, many of us learn new things and think we have a good understanding of it. But then we learn more and realize that there was more to know. So I made a new Drag and Drop that uses better methodologies. Enjoy.

Important info

  1. There exists e.dataTransfer which helps with drag and drop functionality, but as we're using React I find using state to be simpler.

  2. Make sure to check out the Code Sandbox. I may add a few things that aren't reflected below, but the code below is complete.

  3. You might know a better way to do this! If you think you can improve the code please comment.

Drag and Drop in HTML5

There are a few new elements that we will be using (we're not using all of the HTML5 Drag and Drop elements).

  1. draggable makes a div draggable (instead of highlighting)
  2. onDragStart fires when you begin to drag
  3. onDragEnter fires ONCE when the dragged div enters another.
  4. onDragOver fires CONTINUOUSLY when dragging over a div
  5. onDrop fires when the mouse click is released

The final 4 of these we will pass to JavaScript to give it the DND logic.

Getting Started.

Let's make a some groups to drag between and some items to be dragged around.

Dnd.js

import React, { useState } from "react";
import "./Dnd.scss";

export default function Dnd() {

  // my groups to be dragged between

  const groups = ["group1", "group2", "group3", "noDrop"];

  // My items to be dragged around

  const initialItems = [
    { id: 1, group: "group1", value: "drag 1" },
    { id: 2, group: "group1", value: "drag 2" },
    { id: 3, group: "group1", value: "drag 3" }
  ];

  return (
    <>

      // Creating the group divs 

      <div className="groups">
        {groups.map((group) => (
          <div className="group">
            <h1 className="title">{group}</h1>
            <div>

              // Creating our items to drag and drop

              {items
                .filter((item) => item.group === group)
                .map((item) => (
                  <div
                    key={item.id}
                    id={item.id}
                    className="item"

                    // THIS MAKES THE ITEM DRAGGABLE!!!

                    draggable
                  >
                     // item title
                    {item.value}
                  </div>
                ))}
            </div>
          </div>
        ))}
      </div>
    </>
  );
}

Dnd.scss

.groups {
  display: flex;
  margin: 5px;
  padding: 5px;
  flex-wrap: wrap;


  .group {
    margin: 2px;
    padding: 20px;
    min-height: 16rem;
    background-color: green;

    .title{
      color: white;
      padding: 0;
      margin-top: 0;
    }
  }
}


.item {
  background-color: yellow;
  color: blue;
  margin: 5px;
  padding: 5px;
  border: 2px green;
  cursor: grab;
}

This creates something that looks like this:
Image description

Now we'll add the events and event handlers. Make sure to read the comments in the code as that's where the explanations are. I think this is simpler than describing everything.

Hint: The comments are easier to read in the Code Sandbox.

Dnd.js

import React, { useState } from "react";
import "./Dnd.scss";

export default function Dnd() {
  // Initial groups to drag between
  const groups = ["group1", "group2", "group3", "noDrop"];
  // Initial items to be dragged 
  const initialItems = [
    { id: 1, group: "group1", value: "drag 1" },
    { id: 2, group: "group1", value: "drag 2" },
    { id: 3, group: "group1", value: "drag 3" }
  ];
  // Sets the state of the items. I may add an "add" function later
  // Can be used to add items
  const [items, setItems] = useState(initialItems);
  // Data about a things id, origin, and destination
  const [dragData, setDragData] = useState({});
  // Are we hovering over the noDrop div?
  const [noDrop, setNoDrop] = useState("");

  // onDragStart we setDragData.
  // useState instead of e.dataTransfer so we can transfer more data
  const handleDragStart = (e, id, group) => {
    setDragData({ id: id, initialGroup: group });
  };

  // If we enter the noDrop zone the state will be updated
  // Used for styling.
  const handleDragEnter = (e, group) => {
    if (group === "noDrop") {
      setNoDrop("noDrop");
    }
  };

  // DND will not work without this.
  const handleDragOver = (e) => {
    e.preventDefault();
  };

  // setNoDrop to nothing to return styling to normal
  const handleDragLeave = (e) => {
    setNoDrop("");
  };

  // 1. makes copy of items (newItems)
  // 2. changes category of the item to its new group
  // 3. setItem to our NewItems
  const changeCategory = (itemId, group) => {
    const newItems = [...items];
    newItems[itemId - 1].group = group;
    setItems([...newItems]);
  };

  // 1. setNoDrop in case item was dropped in noDrop
  // 2. gets the item id
  // 3. doesn't allow drop in noDrop
  // 4. changeCategory (see above)
  const handleDrop = (e, group) => {
    setNoDrop("");
    const selected = dragData.id;
    if (group === "noDrop") {
      console.log("nuh uh");
    } else {
      changeCategory(selected, group);
    }
  };

  return (
    <>
      <div className="groups">
        {/* iterate over groups */}
        {groups.map((group) => (
          <div
            // change styling if dragging into noDrop zone
            className={`${
              group === "noDrop" && noDrop === "noDrop" ? noDrop : "group"
            }`}
            // event handlers
            onDragEnter={(e) => handleDragEnter(e, group)}
            onDragOver={handleDragOver}
            onDragLeave={handleDragLeave}
            onDrop={(e) => handleDrop(e, group)}
            key={group}
          >
            <h1 className="title">{group}</h1>
            <div>
              {/* iterate over items */}
              {items
                .filter((item) => item.group === group)
                .map((item) => (
                  <div
                    key={item.id}
                    id={item.id}
                    // change style if dragged over noDrop
                    className={`${
                      group === "noDrop" && noDrop === "noDrop"
                        ? "notAllowed"
                        : "item"
                    }`}
                    // MAKES THE ITEM DRAGGABLE!!!!
                    draggable
                    // event handler
                    onDragStart={(e) => handleDragStart(e, item.id, group)}
                  >
                    {/* The name of each item */}
                    {item.value}
                  </div>
                ))}
            </div>
          </div>
        ))}
      </div>
    </>
  );
}

Dnd.scss

.groups {
  display: flex;
  margin: 5px;
  padding: 5px;
  flex-wrap: wrap;


  .group {
    margin: 2px;
    padding: 20px;
    min-height: 16rem;
    background-color: green;

    .title{
      color: white;
      padding: 0;
      margin-top: 0;
    }
  }
  .noDrop {
    margin: 2px;
    padding: 20px;
    min-height: 16rem;
    background-color: red;
    cursor: not-allowed !important;

    .title{
      color: white;
      padding: 0;
      margin-top: 0;
    }
  }
}


.item {
  background-color: yellow;
  color: blue;
  margin: 5px;
  padding: 5px;
  border: 2px green;
  cursor: grab;
}

.notAllowed {
  background-color: yellow;
  color: blue;
  margin: 5px;
  padding: 5px;
  border: 2px green;
  cursor: not-allowed;
}

This is what it looks like

Image description

Conclusion

This is the basic gist of it. If you need something simple that works this is it, otherwise feel free to install a library.

Watch it in action! See the Code Sandbox


This content originally appeared on DEV Community and was authored by Aaron Wolf


Print Share Comment Cite Upload Translate Updates
APA

Aaron Wolf | Sciencx (2021-12-30T12:59:20+00:00) Drag and Drop with React (with no libraries) part 2. Retrieved from https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/

MLA
" » Drag and Drop with React (with no libraries) part 2." Aaron Wolf | Sciencx - Thursday December 30, 2021, https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/
HARVARD
Aaron Wolf | Sciencx Thursday December 30, 2021 » Drag and Drop with React (with no libraries) part 2., viewed ,<https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/>
VANCOUVER
Aaron Wolf | Sciencx - » Drag and Drop with React (with no libraries) part 2. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/
CHICAGO
" » Drag and Drop with React (with no libraries) part 2." Aaron Wolf | Sciencx - Accessed . https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/
IEEE
" » Drag and Drop with React (with no libraries) part 2." Aaron Wolf | Sciencx [Online]. Available: https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/. [Accessed: ]
rf:citation
» Drag and Drop with React (with no libraries) part 2 | Aaron Wolf | Sciencx | https://www.scien.cx/2021/12/30/drag-and-drop-with-react-with-no-libraries-part-2/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.