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.

useDrag with dropEffect can break dragging capability

See original GitHub issue

Describe the bug When using useDrag with specified dropEffect and props (deps array of the useDrag), and deps have changed - it won’t allow to drag anymore after the first drop. Dragging is simply not working for some reason. Removing dropEffect fixes the issue, so seems like it’s a bug.

Reproduction

Live Reproduction

Steps to reproduce the behavior:

  1. Take any example with hooks
  2. Add simple state in Container.jsx file, for example, counter:
import { useState } from react;
// ...
// ... in component code
const [counter, setCounter] = useState(0);
// ...
  1. Pass both counter and setCounter to SourceBox component
  2. In useDrag hook, specify:
// ...
const [collected, drag] = useDrag(() => {
  type: ItemTypes.BOX,
  options: { dropEffect: 'copy' },
  end: () => { setCounter(c => c + 1); },
  // ... rest of the spec code
}, [counter]);
// ...
5. Drag once and drop
6. Verify dragging is not working anymore

**Expected behavior**
The case described above described should not break the dragging behavior.

**Screenshots**
N/A

**Desktop (please complete the following information):**

- OS: macOS
- Browser: Chrome
- Version 96.0.4664.55 (Official Build) (arm64)

**Additional context**
I didn't check if the same bug happens with Decorators, so don't know if it's just a Hooks-specific bug.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:5
  • Comments:8

github_iconTop GitHub Comments

6reactions
omarsourourcommented, Apr 21, 2022

After investigation, the root cause of the issue is that dependencies when changed issue a disconnect of the drag connector, but the reconnect is not fired.

Looking at the file useDrag

image

The deps paramater when changed causes new instances of spec.options and spec.previewOptions to be created. These are passed to the useDragSourceConnector hook, which does the following:

image

Whenever the dragSourceOptions input has a new reference, the hook is re-fired, which means the connector.disconnectDragSource() is called when the dragSourceOptions are changed. That causes the connector to cleanup the old connection by disconnecting it. So far so good. Since the hook is re-fired, after the cleanup, the connector.reconnect() should be fired to re-connect the connector with the source.

However, the problem lies here:

image

The re-connection will only happen if the didChange variable is true. In the case of the same drag element in the HTML with only the deps array changing, the didHandlerIdChange and didConnectedDragSourceChange will evaluate to false since it is the same element. However, we expect the didDragSourceOptionsChange evaluation to be true, since the source options did in fact change due to the change in the deps array.

The didDragSourceOptionsChange does a key-by-key object comparison though using a custom shallowEqual implementation. This causes the evaluation of didDragSourceOptionsChange to be false even the reference changed due to the change in deps in the useDrag hook.

The workaround I did for now is that I added the values of my deps array as keys in the specOptions object. So for example, my code looks like this:

const [{ isDragging }, drag, preview] = useDrag(() => ({
      type: "someType",
      item: { id },
      options: { dropEffect: 'move', dep1, dep2 }, // Note that dep1 and dep2 don't belong as part of the options for the hook
      collect: (monitor) => ({ isDragging: monitor.isDragging() }),
  }), [dep1, dep2]);

Having the dependencies in the options object ensures that the didDragSourceOptionsChange function evaluate to true when the connector.reconnect() is called when the useDrag hook is updated.

My suggestions to a fix for this issue would be either:

  • Use reference equality == inside the didDragSourceOptionsChange function to account for the options reference changed. This would ensure the reavluation happens whenever the source options change whenever the hook is re-evaluated due to any of its dependencies changing. The source options would never change anyway since the default deps value is an empty array.
  • OR, pass down the deps (as a different interface?) to the connector to be evaluated as part of the expression that calculates the didChange variable value.
2reactions
jamesopticommented, May 27, 2022

@omarsourour THANK YOU! This was pretty frustrating to debug, but your solution works great.

Would be great to get this fix merged now that were on 2 major versions past this issue 😕

Read more comments on GitHub >

github_iconTop Results From Across the Web

Developers - useDrag with dropEffect can break dragging capability -
When using useDrag with specified dropEffect and props (deps array of the useDrag ), and deps have changed - it won't allow to...
Read more >
Mastering Drag & Drop with ReactJS | by Shay Keinan
The Draggable component. We are going to create a class component that will give us the ability to wrap other components and make...
Read more >
An Essential Guide to JavaScript Drag and Drop By Examples
This tutorial introduces you to the JavaScript Drag and Drop API and shows you how to handle drag and ... By default, only...
Read more >
How to implement drag and drop in React with React DnD
What is react-dropzone? Display an image preview; Reorder images with drag and drop; React drag and drop libraries; react-beautiful-dnd. react ...
Read more >
HTML5 Drag and Drop - James Priest CV - GitHub Pages
Prior to HTML5, the ability to use drag and drop operations was possible only ... The data is retrieved, split into an array,...
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