Cosmic JS vs. WordPress

Cosmic JS is an API-first CMS that helps teams of developers and content editors build apps faster. 64.5% of developers surveyed on StackOverflow stated that WordPress was one of their most dreaded…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Drag and Drop in React

Using react-dnd

The drag and drop is one the most basic and important actions a person learns when they are first introduced to a computer with a GUI (Graphical User Interface). The first time I remember performing the action is back on my PC running Windows 95 when I dragged a file over my Trash Bin Icon and dropped it in, effectively deleting the file.

The premise has not changed much since then. At its base simplicity, the steps are as follows:

First, install React Dnd and its accompanying HTML5 backend using the following:

Once installed, it will be helpful to familiarize yourself with the way React DnD works as a “middle-man” between React and the DOM.

Lets start with how to DOM Communicates with React-DnD and vice versa.

When you drag something across the screen, its not an element or dom node thats being dragged, we call the dragged object an item. Each item is a plain JavaScript object describing what’s being dragged. Each item must have an Item Type which we use to specify what can be dropped where (for example a container can be set up to only accept items with the item type of “card”).

Lastly, monitors let you update the props of your components in response to the drag and drop state changes. In simpler terms, they “monitor” the state of each object. Is it being dragged? Is something being dragged over it? Was something just dropped on it?

Moving on to the other half of the relay, after a DnD event passes info to React-DnD, it then passes that info along to React, in order to update components, state, or even an external database.

For each component that needs to track the drag and drop state, you can define a Collector, a function that retrieves the relevant bits of information from the monitors.

This allows us to pass the updated values of highlighted and hovered as props to the component the collector is defined inside of.

Finally, we have our Drag Sources & Drop Targets. A drag source is the draggable component. It’s job is to carry the relevant info needed to perform some action to the drop target. The drop target on the other hand is a container which will only accept certain types of drag sources, and upon accepting one, will perform said action.

Alright, enough reading. Let’s actually build something!

I will be implementing the feature in an app I previously built which lets you add ingredients to a cart, and then gives you a list of all recipes that can be made with the ingredients on hand. (Its called “I’m Fridge’n Hungry”). The hierarchy of the app is shown below:

The App component holds the state of the Fridge and all recipes that can be made from fridge contents.

We will be focusing most of our attention on the Grocery Store component, but it is important to understand the relationship between the Grocery Store and Fridge, to understand what we are doing. When an item is selected in the Ingredients Container, it is added to the cart. Upon ‘checkout’ all of the items in the Cart component are transferred to the Fridge. Based on the the items in our Fridge, our Recipes Container, will filter out all of the Recipes for which we do not have all of the necessary ingredients.

Now let’s take a look at our Grocery Store page. What we want to is be able to add an ingredient from our shelf on the left into our cart on the right.

Currently it is working via an onClick function on each individual component like so:

Clicking an ingredient on the shelf adds to the cart. Clickin in the cart removes it.

Now, the first step to making each ingredient draggable is the add Context to the application. We do that by adding the following imports to our App.js file:

Next, we need to wrap all of the components that will need the DnD functionality in the DnDProvider Component we just imported. In my code it looks something like this:

You’ll notice that we also passed in a backend object to the DndProvider component. This is necessary so that it know what type of backend its dealing with (in this case HTML5).

Defining Item Types

In order to make sure only certain items can interact with drag and drop, we need to set up a file where we will define all of our Item Types. The neatest way to do this is to add a Utilities folder to the src folder, and within Utilities create am items.js file. Today, we will only have one item type of “card”, but if you have many different item types this is a good way to handle storing them all.

Our items.js file will look like this:

Now anywhere we need to define an item type, we will require this file like so:

Making Components Draggable

The next step is to make our components draggable. We will want to do this in the Ingredient Card component. To this this we will add the following code to my Ingredient.js file:

This is a lot to add so let's break it down:

First we import the ItemTypes file from /Utilities/items. Then we also need to import the useDrag object from ‘react-dnd’.

In our case the DraggableComponent function will be our Ingredient. We then will definite a spec, which requires and item and item type. Let’s take a look at how I modified this useDrag template above to suit my needs.

In my case, the collectedProps item, becomes the isDragging prop which is provided as a property of monitor. Next, the drag reference immediately after the {isDragging} is always present in this const definition, and is also added in the div of whatever component you want to make draggable. Check my return statement above to see an example of this.

Then, we are required to define an item object, which is just a POJO containing whatever information we want to store about the object being dragged. The item type is ALWAYS required, and in addition I added an id (which is being passed down from props.ingredient.id) and then the entire ingredient object itself (props.ingredient).

Finally, we have our collect function, which should return a plain object of the props to return for injection into your component. It receives two parameters, monitor, and props. In our case we only need access to the monitor which will will use to set the isDragging status to true, when our monitor.isDragging returns true.

Our component is now draggable.

This is great, but nothing happens when we drop!

Make Container Droppable

As expected, nothing happens because we did not make our Cart container droppable. Our draggable components always need a target object!

Now we will go into our Cart.js file and add the following code:

The useDrop ALWAYS requires an accept, which similar to our item.type object in the useDrag, will specify what type of item it can accept.

Then, similar to the useDrag, it will also take in collectedProps, and a drop reference defined via a const.

In my case, my props would be an isOver supplied by the monitor, which will monitor when an object is hovering over the container. If that object’s item type matches the item type type specified in the accept, it will allow us to use the drop attribute.

We also have a drop reference in our spec, which will be used to set the div of the object we want to allow to accept our drop action.

Finally, we again have a collect attribute that allows us to set an isOver to true, when an object is hovering over it. In order to test that this is working, let’s add conditional styling to our border to change the color when a “card” object is dragged over it like so:

Let’s see it in action:

The final step is define what our app does when we drop an object onto a target. In this case, all we need to do is add a drop attribute to our usDrop spec in Cart.js. All we need to do is add this line to our code:

Drop takes in props of item, and monitor, and it say whenever an item which the correct ItemType is dropped on this component, run this function. In our case props.dragHandler, is a function being passed down to the Cart component from our GroceryStore component, which adds the ingredient being dragged to its state.

Let’s see the finished result:

You can drop anywhere in the cart container and it works!

Add a comment

Related posts:

There are No Problems Only Options

The trouble is the time we waste “in” and thinking about our problem is hardly productive. It consumes us and like a stick stuck in a stream, we stay there. The stream flows around us but we don’t…

Trump and Biden Reenact the Battle of the Alamo

This is an unconventional Medium story. The idea to send Donald Trump and Joe Biden back in time to the Battle of the Alamo sprung after the recent “Trump Train” versus “Biden Bus” incident in Texas…

Conclusion

Meta transactions allow apps to abstract away seed phrases and wallet downloads at first. Users can simply use an app and the notion of gas is abstracted away. Then, after they have earned value…