Open Sun-2 opened 3 years ago
Hi there @Sun-2 ! I saw your post and had a similar issue, thought I would post my solution in case it helps.
I was only removing the source item after the drag started. As with you, as soon as my source item is removed the touch would 'pause'. I would need to lift my finger and then tap once more to make the drag end event happen.
For me (and maybe for you!) this happened because touchmove
events REQUIRE the original source item to stay mounted. See the answer in this post, and the associated links they add: https://stackoverflow.com/questions/56653453/why-touchmove-event-is-not-fired-after-dom-changes.
I solved the issue by 'caching' the source item in the list where it was being removed, a bit like this (shortened my code because there's lots of other logic going on in that component -- please ask if this is not clear):
const MyList = ({ items }) => {
// items: (array of strings) identifying item ids in my list
const removedItemRef = React.useRef(null);
// Add cached removed item back into list
// Despite it not being in 'items' passed to this component
if (removedItemRef.current){
if (!items.includes(removedItemRef.current)){
items = [ ...items, removedItemRef.current ]
}
}
const renderItem = (item, index) => {
return (
<DraggableItem
itemId={item}
handleDragStart={() => {
// When dragging starts, cache the id
removedItemRef.current = item;
}}
handleDragEnd={() => {
// When dragging ends, clear cached id
removedItemRef.current = null;
}}
/>
);
}
return (
<div>
{items.map(renderItem)}
</div>
);
}
Expected behavior
The backend is
react-dnd-touch-backend
, the testing is done on an actual phone, and with Chrome's devtools.I've got an application with
react-dnd
andreact-router-dom
. If the user starts dragging an item on route/drag
, ahistory.push
to/drop
is issued, where the item can be dropped. Therefore, on drag start, the source is unmounted, and the target is mounted.Since the issue is reproducible just with
useState
instead ofreact-router-dom
, I've used it for simplicity. The idea is the same:isDragging
useState
.useDrag
, on drag start it callsprops.setIsDragging(true)
isDragging === true ? <DropZone /> : <Draggable />
props.isDragging(false)
, and thus we return to the draggable's view.Actual behavior
useDrag
'sbegin
fires properly.isDragging
is set to true, thereforeDropZone
is rendered instead ofDraggable
.DropZone
or not - just nothing happens. The application just keepsDropZone
rendered. To trigger the drop end, the user must tap another time.Steps to reproduce
git clone https://github.com/Sun-2/react-dnd-mount-drop-bug && cd react-dnd-mount-drop-bug && npm i && npm run start
Versions
"react": "^17.0.1",
What browser are you using?
Chrome 88.0.4324.150 (Official Build) (64-bit) OS: Ubuntu 20.10
Demo
useDrag
throws withnextCreate is not a function
in CodeSandbox, therefore here's a GitHub repo. The link leads to the only relevant file.