react-beautiful-dnd
react-beautiful-dnd copied to clipboard
On mobile, re-render the dragged element while it is being dragged. Need to drag twice to drag.
Expected behavior
Drag success
Actual behavior
The first drag and drop will not work, Two clicks to drag to drag
Steps to reproduce
- use demo
- open devtools Switch to Mobile mode
- drag clone item filed
Suggested solution?
I'm guessing it's due to re-rendering the drag while dragging. use “isDragging” in render, “isDragging” change causes the view to render
What version of React
are you using?
same as demo
What version of react-beautiful-dnd
are you running?
same as demo
What browser are you using?
chrome
Demo
https://react-beautiful-dnd.netlify.app/?path=/story/board--with-combining-and-cloning
same issue. thats terrible. for this, i have to change a plugin. touch event is very very bad. just tap is good, not long press
I also encountered this problem when the mode parameter is "virtual".
In this mode, the node being dragged in the ‘virtual list’ will be cloned into a new node, while the old node is deleted. I went into the source code to debug and found that the touchmove and mousemove events could not be triggered after touchstart was triggered.
So after much searching, I found this issue on StackOverflow: https://stackoverflow.com/questions/33298828/touch-move-event-dont-fire-after-touch-start-target-is-removed
If the target element is removed from the document, events will still be targeted at it, and hence won't necessarily bubble up to the window or document anymore. If there is any risk of an element being removed while it is being touched, the best practice is to attach the touch listeners directly to the target.
Let’s go back to this issue of needing to drag twice on mobile. In the documentation:https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/sensors/sensor-api.md, there is a ‘Sensor API’ that allows us to customize how to respond to user interactions. First, we need to disable the default ‘Sensor’ On ‘DragDropContext’, set ‘enableDefaultSensors’ to ‘false’.
import {
DragDropContext,
SensorAPI
} from 'react-beautiful-dnd'
function useTouchSensor(api: SensorAPI) {}
function YourComponent() {
return <DragDropContext enableDefaultSensors={false} sensors={[useTouchSensor]} />
}
Then we can use ‘useTouchSensor’ from the source code, copy it out, and make modifications.
https://github.com/atlassian/react-beautiful-dnd/blob/master/src/view/use-sensor-marshal/sensors/use-touch-sensor.js
We already know that the problem is that the event source has been deleted, so it can’t bubble up to the window later, and the events bound on the window can’t be triggered. So, we need to bind the touchmove event to the event source.
We can pass the event object through the touchstart event to where the touchmove event is bound.
You can also modify the ‘timeForLongPress’ variable to make it 0, so you don’t have to press and hold.
Please note this comment in the source code:
// In prior versions of iOS it was required that touch listeners be added
// to the handle to work correctly (even if the handle got removed in a portal / clone)
// In the latest version it appears to be the opposite: for reparenting to work
// the events need to be attached to the window.
// For now i'll keep these two functions seperate in case we need to swap it back again
// Old behaviour:
// https://gist.github.com/parris/dda613e3ae78f14eb2dc9fa0f4bfce3d
// https://stackoverflow.com/questions/33298828/touch-move-event-dont-fire-after-touch-start-target-is-removed
I have only tested this in the mobile simulator on the Chrome desktop end and it works, but I haven’t tested it on other devices yet. So if there are any issues, you can try to write a custom Sensor like I did.
You can see solution in: https://github.com/atlassian/react-beautiful-dnd/pull/2281/files
I stumbled upon this issue today when trying to clone my draggable element for the first time. I was so happy it was working just fine in my browser that I completely forgot of mobile devices. After some attempts, I went to the repo issues to find out this was actually a common issue and it seemed hopeless... until I found @kawayiLinLin's answer. At first, I refused to copy the useTouchSensor()
code, but after giving it some thoughts, I had nothing to lose (except time to migrate types from Flow
to TypeScript
).
These are the files I copied from react-beautiful-dnd
to my source code:
- useTouchSensor (indeed)
- Event Bindings
- Invariant
- Key Codes
- Supported Page Visibility Event Name
Disclaimer: if anyone knows a better way to solve this issue, for instance by providing another solution or even reduce boilerplate code, it would be very welcome!
Furthermore, these are the specific changes you must address in your useTouchSensor()
code to prevent double dragging behavior on mobile devices.
Finally, as @kawayiLinLin pointed out, make sure to update your DragDropContext
component to use new sensor. Note that I added useMouseSensor
from react-beautiful-dnd
because I still need that sensor to work.
<DragDropContext
enableDefaultSensors={false}
sensors={[useMouseSensor, useTouchSensor]}
>
I've tested its behavior on the following browsers, all of them working fine:
- Brave Desktop [macOS 14.1.2 / M1] (Version 1.61.114 Chromium: 120.0.6099.199)
- Brave Mobile [iOS 17.0.2 / iPhone 11] (Version 1.61 BraveCore 1.61.102)
- Firefox Mobile [iOS 17.0.2 / iPhone 11] (Version 121.1 (37312))
- Safari Mobile [iOS 17.0.2 / iPhone 11] (Version 17.0.2)