Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
react-dnd
Advanced tools
The react-dnd package is a set of React utilities to help you build complex drag and drop interfaces while keeping your components decoupled. It provides a way to use drag and drop features in React by turning any React component into a draggable or a droppable area.
Draggable Components
This feature allows you to turn any component into a draggable item. The useDrag hook is used to make components draggable and to monitor their state.
{"import { useDrag } from 'react-dnd';\n\nfunction DraggableComponent() {\n const [{isDragging}, dragRef] = useDrag({\n type: 'BOX',\n collect: monitor => ({\n isDragging: !!monitor.isDragging(),\n }),\n });\n\n return (\n <div ref={dragRef} style={{ opacity: isDragging ? 0.5 : 1 }}>\n Drag Me\n </div>\n );\n}"}
Droppable Areas
This feature allows you to define areas where draggable items can be dropped. The useDrop hook is used to handle the drop events and to determine the state of the drop targets.
{"import { useDrop } from 'react-dnd';\n\nfunction DroppableComponent() {\n const [{canDrop, isOver}, dropRef] = useDrop({\n accept: 'BOX',\n drop: () => ({ name: 'DroppableComponent' }),\n collect: monitor => ({\n isOver: monitor.isOver(),\n canDrop: monitor.canDrop(),\n }),\n });\n\n return (\n <div ref={dropRef} style={{ backgroundColor: isOver ? 'green' : 'white' }}>\n {canDrop ? 'Release to drop' : 'Drag a box here'}\n </div>\n );\n}"}
Custom Drag Layer
This feature allows you to create a custom drag layer that can be used to display a custom preview of the draggable item. The useDragLayer hook provides information about the item being dragged and its position.
{"import { useDragLayer } from 'react-dnd';\n\nfunction CustomDragLayer() {\n const { isDragging, item, currentOffset } = useDragLayer((monitor) => ({\n item: monitor.getItem(),\n currentOffset: monitor.getSourceClientOffset(),\n isDragging: monitor.isDragging(),\n }));\n\n if (!isDragging) {\n return null;\n }\n\n return (\n <div style={{ position: 'fixed', pointerEvents: 'none', left: currentOffset.x, top: currentOffset.y }}>\n {/* Render custom drag layer here */}\n </div>\n );\n}"}
react-beautiful-dnd is a higher-level abstraction built on top of the lower-level primitives provided by react-dnd. It offers a more opinionated but simpler API and focuses on vertical and horizontal lists. It provides beautiful and accessible drag and drop for lists with React.
react-sortable-hoc is a set of higher-order components to turn any list into an animated, touch-friendly, sortable list. It's more focused on list sorting capabilities and less on general drag and drop scenarios compared to react-dnd.
dnd-kit is a modern drag and drop toolkit for React that is designed to be more performant and flexible than react-dnd. It provides hooks and utilities to build complex drag and drop interfaces and supports multiple backends, including touch and keyboard.
HTML5 drag-and-drop mixin for React with full DOM control.
Check these first and see if they fit your use case.
If they don't, read on.
After cloning the project, run:
npm install
npm start
open http://localhost:8080/
This will run the included examples.
Or you can view them in browser (and see their source code).
The library can be used separately (dist/ReactDND.min.js
) or with a bundler such as Webpack or Browserify.
npm install --save react-dnd
Dependencies: Flux and a couple of functions from lodash-node;
Peer Dependencies: React >= 0.12.0.
It is written in ES6 but there's an ES5 version in dist-modules
folder.
The ES5 version is what you get when you require('react-dnd')
.
The examples use ES6.
Existing drag-and-drop libraries didn't fit my use case so I wrote my own. It's similar to the code we've been running for about a year on Stampsy.com, but rewritten to take advantage of React and Flux.
Key requirements:
Hopefully the resulting API reflects that.
First, declare types of data that can be dragged. This library, in vein of native drag and drop API, emphasizes dragging data and not specific DOM nodes themselves. Drag sources provide data to be dragged, and drag targets choose to either accept or decline data depending on its type.
It is up to you to update your models in response to drop or other drag events. The library won't touch the DOM nodes. You have full control over your DOM. This makes react-dnd very flexible: you can implement selective drop targets, 2D dragging on a plane or a sortable with the same set of tools.
String “types” are used to check compatibility of drag sources and drop targets:
// A sample ItemTypes.js enumeration for an app where you can drag images and blocks
module.exports = {
BLOCK: 'block',
IMAGE: 'image'
};
These types are just string constants that are used to match compatible drag sources and drop targets. Even if you only plan to have one draggable type of items, it's still neccessary to declare a string constant for it. This makes it trivial to later add additional draggable/droppable types without rewriting half of your drag and drop code. Also, always relying on types allows us to elegantly support file drag and drop via a “builtin” NativeDragItemTypes.FILE
type.
Let's make a very simple draggable component that, when dragged, represents IMAGE
:
/**
* Don't panic!
* Examples will use ES6 syntax.
*
* var { DragDropMixin } = require('react-dnd');
*
* is equivalent to this in ES5:
*
* var DragDropMixin = require('react-dnd').DragDropMixin;
*/
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var Image = React.createClass({
mixins: [DragDropMixin],
statics: {
configureDragDrop(registerType) {
// Specify all supported types by calling registerType(type, { dragSource?, dropTarget? })
registerType(ItemTypes.IMAGE, {
// dragSource, when specified, is {
// beginDrag(component),
// canDrag(component)?,
// endDrag(component, dropEffect)?
// }
dragSource: {
// beginDrag should return {
// item,
// dragAnchors?,
// dragPreview?,
// dragEffect?
// }
beginDrag(component) {
return {
item: component.props.image
};
}
}
});
}
},
render() {
// {...this.dragSourceFor(ItemTypes.IMAGE)} will expand into
// { draggable: true, onDragStart: (handled by mixin), onDragEnd: (handled by mixin) }.
return (
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
);
}
);
By specifying configureDragDrop
in statics
, we tell DragDropMixin
the drag-drop behavior of this component. Both draggable and droppable components use the same mixin.
Inside configureDragDrop
, we need to call registerType
for each of our custom ItemTypes
that component supports. For example, there might be several representations of images in your app, and each would provide a dragSource
for ItemTypes.IMAGE
.
A dragSource
is just an object specifying how the drag source works. You must implement beginDrag(component)
to return item
that represents the data you're dragging and, optionally, a few options that adjust the dragging UI. You can optionally canDrag(component)
to forbid dragging, or endDrag(component, dropEffect)
to execute some logic when the drop has (or has not) occured. And you can share this logic between components by letting a shared mixins generate dragSource
for them.
Finally, you must use {...this.dragSourceFor(itemType)}
on some (one or more) elements in render
to attach drag handlers. This means you can have several “drag handles” in one element, and they may even correspond to different item types. (If you're not familiar with JSX Spread Attributes syntax, check it out).
Let's say we want ImageBlock
to be a drop target for IMAGE
s. It's pretty much the same, except that we need to give registerType
a dropTarget
implementation:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
statics: {
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
// dropTarget, when specified, is {
// acceptDrop(component, item)?,
// canDrop(component, item)?,
// enter(component, item)?,
// over(component, item)?,
// leave(component, item)?
// }
dropTarget: {
acceptDrop(component, image) {
// Do something with image! For example,
DocumentActionCreators.setImage(component.props.blockId, image);
}
}
});
}
},
render() {
// {...this.dropTargetFor(ItemTypes.IMAGE)} will expand into
// { onDragEnter: (handled by mixin), onDragOver: (handled by mixin), onDragLeave: (handled by mixin), onDrop: (handled by mixin) }
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{this.props.image &&
<img src={this.props.image.url} />
}
</div>
);
}
);
Say we now want the user to be able to drag out an image out of ImageBlock
. We just need to add appropriate dragSource
to it and a few handlers:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
statics: {
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
// Add a drag source that only works when ImageBlock has an image:
dragSource: {
canDrag(component) {
return !!component.props.image;
},
beginDrag(component) {
return {
item: component.props.image
};
}
},
dropTarget: {
acceptDrop(component, image) {
DocumentActionCreators.setImage(component.props.blockId, image);
}
}
});
}
},
render() {
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{/* Add {...this.dragSourceFor} handlers to a nested node */}
{this.props.image &&
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
}
</div>
);
}
);
The library provides one built-in item type: NativeDragItemTypes.FILE
. You can't set up a drag source for it, but you can set up as many drop targets as you want with the same API as normal drop targets. The only way in which file drop target differs from a normal one is that item
parameter will always be null
in enter
, over
and leave
. In drop
, its files
property will contain a JS array of filenames as given by browser.
var { DragDropMixin, NativeDragItemTypes } = require('react-dnd');
var ImageUploader = React.createClass({
mixins: [DragDropMixin],
statics: {
configureDragDrop(registerType) {
registerType(NativeDragItemTypes.FILE, {
dropTarget: {
acceptDrop(component, item) {
// Do something with files
console.log(item.files);
}
}
});
}
},
render() {
var fileDropState = this.getDropState(NativeDragItemTypes.FILE);
return (
<div {...this.dropTargetFor(NativeDragItemTypes.FILE)}>
{fileDropState.isDragging && !fileDropState.isHovering &&
<p>Drag file here</p>
}
{fileDropState.isHovering &&
<p>Release to upload a file</p>
}
</div>
);
}
);
I have not covered everything but it's possible to use this API in a few more ways:
getDragState(type)
and getDropState(type)
to learn if dragging is active and use it to toggle CSS classes or attributes;dragPreview
to be Image
to use images as drag placeholders (use ImagePreloaderMixin
to load them);ImageBlock
s reorderable. We only need them to implement dropTarget
and dragSource
for ItemTypes.BLOCK
.dropTargetFor(...types)
allows to specify several types at once, so one drop zone can catch many different types.require('react-dnd').DragDropMixin
statics.configureDragDrop(registerType)
Gives you a chance to configure drag and drop on your component.
Components with DragDropMixin
will have this method.
registerType(type, { dragSource?, dropTarget? })
Call this method to specify component behavior as drag source or drop target for given type.
This method is passed as a parameter to configureDragDrop
.
getDragState(type)
Returns { isDragging: bool }
describing whether a particular type is being dragged from this component's drag source. You may want to call this method from render
, e.g. to hide an element that is being dragged.
getDropState(type)
Returns { isDragging: bool, isHovering: bool }
describing whether a particular type is being dragged or hovered, when it is compatible with this component's drop source. You may want to call this method from render
, e.g. to highlight drop targets when they are comparible and when they are hovered.
dragSourceFor(type)
Returns props to be given to any DOM element you want to make a drag source. Intended to be used with JSX spread attribute syntax.
dropTargetFor(types...)
Returns props to be given to any DOM element you want to make a drop target. Intended to be used with JSX spread attribute syntax.
===================
Implement to specify drag behavior of a component:
beginDrag(component: ReactComponent, e: SyntheticEvent)
— return value must contain item: Object
representing your data and may also contain dragPreview: (Image | HTMLElement)?
, dragAnchors: { horizontal: HorizontalDragAnchors?, vertical: VerticalDragAnchors? }?
, effectsAllowed: DropEffects[]?
.
canDrag(component: ReactComponent, e: SyntheticEvent)
— optionally decide whether to allow dragging.
endDrag(component: ReactComponent, effect: DropEffect?, e: SyntheticEvent)
— optionally handle end of dragging operation. effect
is falsy if item was dropped outside compatible drop targets, or if drop target returned null
from getDropEffect()
.
===================
To perform side effects in response to changing drag state, use these methods:
enter(component: ReactComponent, item: Object, e: SyntheticEvent)
leave(component: ReactComponent, item: Object, e: SyntheticEvent)
over(component: ReactComponent, item: Object, e: SyntheticEvent)
For example, you might use over
for reordering items when they overlap. If you need to render different states when drop target is active or hovered, it is easier to use this.getDropState(type)
in render
method.
Implement these methods to specify drop behavior of a component:
canDrop(component: ReactComponent, item: Object): Boolean
— optionally implement this method to reject some of the items.
getDropEffect(component: ReactComponent, effectsAllowed: DropEffect[]): DropEffect?
— optionally implement this method to specify drop effect that will be used by some browser for cursor, and will be passed to drag source's endDrag
. Returned drop effect must be one of the effectsAllowed
specified by drag source or null
. Default implementation returns effectsAllowed[0]
.
acceptDrop(component: ReactComponent, item: Object, e: SyntheticEvent, isHandled: bool, effect: DropEffect?)
— optionally implement this method to perform some action when drop occurs. isHandled
is true
if some child drop target has already handled drop. effect
is the drop effect you returned from getDropEffect
, or if isHandled
is true
, drop effect of the child drop target that handled the drop.
===================
require('react-dnd').ImagePreloaderMixin
You can optionally specify images to be used as drag thumbnails.
Browsers can't do this reliably until image is loaded, so ImagePreloaderMixin
provides an API to do just that:
mixins: [DragDropMixin, ImagePreloaderMixin],
// This method should return array of image urls for preloading
getImageUrlsToPreload() {
return ['some-img-url1', 'some-img-url2'];
},
// You can now use `this.hasPreloadedImage(url)` and `this.getPreloadedImage(url)` in your `dragSource`:
statics: {
configureDragDrop(registerType) {
registerType(ItemTypes.MY_ITEM, {
dragSource: {
canDrag(component) {
return component.hasPreloadedImage('some-img-url1');
},
beginDrag(component) {
return {
item: ...,
dragPreivew: component.getPreloadedImage('some-img-url1');
};
}
}
});
}
}
Above code will load the images after componentDidMount
is executed, and cache them until component unmounted. In componentDidUpdate
, mixin will check if getImageUrlsToPreload
has changed, and load images again if needed.
Note that, for best results, you want to use this.getDragImageScale()
. It will return correct scale to download for your images, considering browser differences in handling Retina screens (should either return 1
or window.devicePixelRatio
). You can either use it with a custom server image resizer, or to choose between normal and @2x
versions of predefined images.
require('react-dnd').NativeDragItemTypes
Provides a single constant, NativeDragItemTypes.FILE
, that you can use as an item type for file drop targets.
require('react-dnd').DropEffects
Provides constants to be passed in effectsAllowed
array from beginDrag()
and returned from drop target's getDropEffect()
. Correponds to singular native dataTransfer.dropEffect
values.
This library is a React port of an API, parts of which were originally written by Andrew Kuznetsov.
<small>0.6.4 (2015-02-10)</small>
FAQs
Drag and Drop for React
The npm package react-dnd receives a total of 1,675,117 weekly downloads. As such, react-dnd popularity was classified as popular.
We found that react-dnd demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.