question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

connectDragPreview remains a mystery

See original GitHub issue

I’ve been reviewing the documentation and github issues for the past hour and connectDragPreview still remains a mystery to me. I would like to define a custom preview for how the item appears when it is being dragged (just like the tutorial example with the horse image). Here is my code:

export const tokenCollector: DragSourceCollector = (connect, monitor) => {
  return {
    connectDragSource: connect.dragSource(),
    connectDragPreview: connect.dragPreview(),
    isDragging: monitor.isDragging()
  };
};

class TokenClass extends React.Component<TokenProps> {
  componentDidMount() {
    const { connectDragPreview } = this.props;
    const img = new Image();
    img.src = '<encoded image>';
    img.onload = () => connectDragPreview(<div>{img}</div>);
  }
  render() {
    const { connectDragSource, isDragging, children } = this.props;
    return connectDragSource(
      <div style={{ opacity: isDragging ? 0.5 : 1 }}>
        {children}
      </div>
    );
  }
}
const dragSource = DragSource(DropType.Token, tokenSpec, tokenCollector)(TokenClass);
export { dragSource as Token };

The standard preview appears with this code.

I then tried to wrap my connectDragSource in my component’s render() method with connectDragPreview, but that only appears to change the drag source where it was picked up from, not how it appears as it’s being dragged.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:3
  • Comments:11 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
darthtrevinocommented, Mar 26, 2019

After wrestling with this problem myself, I found this was due to a limitation in the HTML5 Drag-and-Drop API.

You cannot define arbitrary, detached DOM to write up a drag preview. You have to use an image.

To make this easier, we’ve added the DragPreviewImage component in the top-level API.

2reactions
atkalliecommented, May 27, 2018

Like @augbog said, under the hood all that React DnD is doing is using setDragImage. CustomDragLayer tries to get around this by setting the drag preview image to an empty image. This is explicitly stated in the docs:

The browser APIs provide no way to change the drag preview or its behavior once drag has started. Libraries such as jQuery UI implement the drag and drop from scratch to work around this, but react-dnd only supports browser drag and drop “backend” for now, so we have to accept its limitations.

We can, however, customize behavior a great deal if we feed the browser an empty image as drag preview. This library provides a DragLayer that you can use to implement a fixed layer on top of your app where you’d draw a custom drag preview component.

This is accomplished by using the getEmptyImage utility function that’s included in the react-dnd-html5-backend package. You can see this in action in the CustomDragLayer example provided:

componentDidMount() {
	// Use empty image as a drag preview so browsers don't draw it
	// and we can draw whatever we want on the custom drag layer instead.
	this.props.connectDragPreview(getEmptyImage(), {
		// IE fallback: specify that we'd rather screenshot the node
		// when it already knows it's being dragged so we can hide it with CSS.
		captureDraggingState: true,
	})
}

You are then responsible for translating the CustomDragLayer yourself. This can be seen in the following code excerpt (taken from this file in the same example as earlier):

function getItemStyles(props) {
	const { initialOffset, currentOffset } = props
	if (!initialOffset || !currentOffset) {
		return {
			display: 'none',
		}
	}

	let { x, y } = currentOffset

	if (props.snapToGrid) {
		x -= initialOffset.x
		y -= initialOffset.y
		;[x, y] = snapToGrid(x, y)
		x += initialOffset.x
		y += initialOffset.y
	}

	const transform = `translate(${x}px, ${y}px)`
	return {
		transform,
		WebkitTransform: transform,
	}
}

The CustomDragLayer is also not configured for a specific item type (like DragSource is), so you have to specifically check which item type is being dragged and then decide whether or not the CustomDragLayer should be displayed (following is from same file as above):

renderItem(type, item) {
	switch (type) {
		case ItemTypes.BOX:
			return <BoxDragPreview title={item.title} />
		default:
			return null
	}
}

Kind of a mess if you ask me. I’ve had a bit more success in the past by using the dom-to-image library to turn the DOM node that I want to use as the drag preview into an image and then passing the resulting image to the connectDragPreview function. Something like:

// Some node you want to use as your drag preview
const node = document.createElement("p");
const childNode = document.createTextNode("This is new.");
node.appendChild(childNode);

domtoimage.toPng(node)
.then(function (dataUrl) {
        const img = new Image();
	img.src = dataUrl;
	img.onload = () => this.props.connectDragPreview(img);
})
.catch(function (error) {
       console.error('something went wrong!', error);
});
Read more comments on GitHub >

github_iconTop Results From Across the Web

How does react-dnd's connectDragPreview() work?
I've been reviewing the documentation and github issues and connectDragPreview still remains a mystery to me.
Read more >
DragPreviewImage - React DnD
New to React DnD? Read the overview before jumping into the docs. DragPreviewImage. A Component to render an HTML Image element as a...
Read more >
Sota (@Sotq_17) / Twitter
I've been reviewing the documentation and github issues for the past hour and connectDragPreview still remains a mystery to me.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found