useDrag with dropEffect can break dragging capability
See original GitHub issueDescribe 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:
- Take any example with hooks
- Add simple state in Container.jsx file, for example, counter:
import { useState } from react;
// ...
// ... in component code
const [counter, setCounter] = useState(0);
// ...
- Pass both
counterandsetCounterto SourceBox component - In
useDraghook, 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:
- Created 2 years ago
- Reactions:5
- Comments:8
Top 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 >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

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
The
depsparamater when changed causes new instances ofspec.optionsandspec.previewOptionsto be created. These are passed to theuseDragSourceConnectorhook, which does the following:Whenever the
dragSourceOptionsinput has a new reference, the hook is re-fired, which means theconnector.disconnectDragSource()is called when thedragSourceOptionsare 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, theconnector.reconnect()should be fired to re-connect the connector with the source.However, the problem lies here:
The re-connection will only happen if the
didChangevariable is true. In the case of the same drag element in the HTML with only thedepsarray changing, thedidHandlerIdChangeanddidConnectedDragSourceChangewill evaluate to false since it is the same element. However, we expect thedidDragSourceOptionsChangeevaluation to be true, since the source options did in fact change due to the change in thedepsarray.The
didDragSourceOptionsChangedoes a key-by-key object comparison though using a customshallowEqualimplementation. This causes the evaluation ofdidDragSourceOptionsChangeto be false even the reference changed due to the change indepsin theuseDraghook.The workaround I did for now is that I added the values of my
depsarray as keys in thespecOptionsobject. So for example, my code looks like this:Having the dependencies in the options object ensures that the
didDragSourceOptionsChangefunction evaluate to true when theconnector.reconnect()is called when theuseDraghook is updated.My suggestions to a fix for this issue would be either:
==inside thedidDragSourceOptionsChangefunction 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.didChangevariable value.@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 😕