React-dnd: How to debug "Cannot call hover after drop"?

Created on 19 Apr 2016  ·  24Comments  ·  Source: react-dnd/react-dnd

Hi,
one of my coworkers is able to somehow break the monitor state and it thinks its hovering even though the mouse is up and gives him this:

screenshot 2016-04-19 16 34 56

Any ideas on where to start debugging? I can't reproduce it and he sees it once a week.
I am using the touch backend switched to handle mouse events as well with a few tweaks.

triage wontfix

Most helpful comment

I'm having this issue as well - my situation is such that the React component is both the drop target and the drag source. When I have two components, I can drag and drop once before it throws the above error. When I have three, it works without an error. I assume it has something to do with the multiple contexts on a single component?

All 24 comments

So investigated the JS issue I found out that prior to this error, there was an error thrown from a user defined callback when drop is supposed to happen, which then could have broken the state of the monitor/store.

I'm having this issue as well - my situation is such that the React component is both the drop target and the drag source. When I have two components, I can drag and drop once before it throws the above error. When I have three, it works without an error. I assume it has something to do with the multiple contexts on a single component?

I had this problem and I found a workaround for my particular use case last night.

For context, I was rendering a list of DragSources, and if any of those dropped onto a DropTarget they would be removed from the list of DragSources and the DropTarget would be replaced with a component which renders the content of the DragSource.

An essential part of this drag and drop functionality (It's a component to match 'categories' to items) was a feature to add items back to the DragSource list, which would just replace the placed item with a DragSource and add it back to the DragSources list.

image

Now, this exception only occurred when I would place items in all the categories and remove at least one (not when the list wasn't full and I would remove one and add it back again). So I came to the conclusion that the DragSources list could never have zero children for this to occur (possibly due to the container element disappearing but I didn't have enough time to test this). To mitigate this, instead of removing items from the DragSources list on placement, I just set their style to display: none

It's a difficult one to explain, but I hope this helps.

I am experiencing the same effect as @PendragonDevelopment. My list starts with one item, gets a second added to it, then can be rearranged. You can only rearrange the two items one time before you start seeing Javascript errors, and you can never reorder them again.

I have this issue, too
I don't know why but after first rearrangement, I see these errors

I got the same error here.

After some comparing the demo example and mine i found that when I loop the Components I use the array key to set component both key and index and in the demo they set only the index Sortable/Simple/Container.js and the key is fixed.

Did it and worked!

The key must be fixed value because if is not the reference to component is loosed after the array reorder.

When you have dynamically component creating and attaching DnD you must use some library or maybe Date.now() will do it for generating unique keys for each component.

{this.state.rows.map(function(row, key) {
  return (<RowComponent key={row.id} index={key} id={row.id} moveRow={that.moveRow} />);
})}

row.id is unique for each component

I don't understand why it works, but using my model.id instead of a random key generated by node-uuid's v4 fixed it. At least I'm not getting errors anymore.

Fixing key didn't solve my problem.
This was my code

           <ContentPatch>
             {tasks.loading 
              ? <div>...loading </div>
              : this.state.containers.map((item, i) => {
                  return (
                    <TaskStage
                      key={item.id}
                      item={item}
                      tasklist={tasks.tasks}
                      onDropped={this.handleDropped}
                      onBeginningDrag={this.onBeginningDrag}
                    />
                  );
                })}
          </ContentPatch>

After every dropped action, I was mapping all items and I was getting same error.
And I changed my condition to

...
{tasks.loading && tasks.tasks.length===0
 ? <div>...loading </div>
...

and it's solved. I think, again again mounting is reason of this error.

I came across this same error.

My use case was:

  • Same DragSource and DragTarget component
  • On drop, I wanted to navigate to a route

Turns out that endDrag (DragSource method) fires after drop (DropTarget method). I was handling the route navigation within drop which was breaking the monitor's state.

Moved that logic over to endDrag to fix it. Refactoring involved checking if the drop was complete with monitor.didDrop(), but wasn't too bad.

I have this issue, too. My case is such that the component is both the drop target and the drag source. I have tried to use endDrag method and tried to use patched backend (https://gist.github.com/nickpresta/eb5cce69d650db4c2795). It didn't solved this problem.

my component:

@DropTarget<HeadColOwnProps>(TaskDndTypes.headCol, headColTargetSpec, headColTargetCollector)
@DragSource<HeadColOwnProps>(TaskDndTypes.headCol, headColSourceSpec, headColSourceCollector)
class HeadColComponent extends React.Component<any, void> {
    render() {
        const props = this.props;
        return this.props.dndConnectDropTarget(
            this.props.dndConnectDragPreview(
                <div>
                    <div className={block('panels-task__drag')({start: props.dndIsDragging})}>
                        <SortingIcon
                            label={props.label}
                            arrowIsVisible={props.sortingIsPossible}
                            direction={props.sortingDirection}
                            clickHandler={props.sortingHandler}
                        />
                        {this.props.dndConnectDragSource(
                            <span className="panels-task__drag-control">
                                <SVGIcon width={10} height={10} url={'#icon-drag-and-drop-cell'} />
                            </span>
                        )}
                    </div>
                </div>
            )
        );
    }
}

Example of use:

const renderHeadCellId = (): JSX.Element => {
        return (
            <TaskCellHead key="key-head-col-id" modifications={{ number: true }}>
                <HeadColComponent
                    label="#"
                    key="key-dnd-head-col-id"
                    taskColType={TaskCols.id}
                    sortingIsPossible={false}
                    taskColsOrder={taskStore.orderCols}
                    updateDragProcess={(dp: TaskColDragProcess | null) => taskStore.updateDragProcess(dp)}
                    updateOrderCols={(order: TaskCols[]) => taskStore.updateOrderCols(order)}
                    dragProcess={taskStore.dragProcess}
                />
            </TaskCellHead>
        );
    };

Decorator settings:

const headColSourceSpec: DragSourceSpec<HeadColOwnProps> = {
    beginDrag(props: HeadColOwnProps): DraggedItem {
        return { sourceColType: props.taskColType };
    },
    canDrag(props: HeadColOwnProps): boolean {
        return props.taskColsOrder.length > 1;
    },
    endDrag(props: HeadColOwnProps, monitor: DragSourceMonitor): void {
        console.debug('endDrag');
        if (!monitor.didDrop()) {
            return;
        }
        console.debug('endDrag finish');
        props.updateOrderCols((monitor.getDropResult() as DroppedResult).newOrderCols);
    }
};

const headColTargetSpec: DropTargetSpec<HeadColOwnProps> = {
    drop(props: HeadColOwnProps, monitor: DropTargetMonitor): DroppedResult {
        console.debug('drop');
        return {
            newOrderCols: getNewOrder((monitor.getItem() as DraggedItem).sourceColType, props.taskColsOrder, props.dragProcess)
        };
    },
    hover(props: HeadColOwnProps, monitor: DropTargetMonitor, component: HeadColComponent): Object | void {
        if (!monitor.canDrop()) {
            return;
        }
        // ...
        props.updateDragProcess(currentDragProcess);
    },
    canDrop(props: HeadColOwnProps, monitor: DropTargetMonitor): boolean {
        return props.taskColType !== (monitor.getItem() as DraggedItem).sourceColType;
    }
};

const headColSourceCollector = (connect: DragSourceConnector, monitor: DragSourceMonitor) => ({
    dndConnectDragSource: connect.dragSource(),
    dndConnectDragPreview: connect.dragPreview(),
    dndIsDragging: monitor.isDragging()
});

const headColTargetCollector = (connect: DropTargetConnector, monitor: DropTargetMonitor) => {
    return {
        dndConnectDropTarget: connect.dropTarget(),
        dndIsOverCurrent: monitor.isOver({ shallow: true })
    };
};

I noticed that after rearrangement endDrag is not called, but drop is called. These lines are never executed:

endDrag(props: HeadColOwnProps, monitor: DragSourceMonitor): void {
        console.debug('endDrag');
        if (!monitor.didDrop()) {
            return;
        }
        console.debug('endDrag finish');
        props.updateOrderCols((monitor.getDropResult() as DroppedResult).newOrderCols);
    }

What am I doing wrong? Any ideas?

I replaced HTML5Backend to Touch Backend (https://github.com/yahoo/react-dnd-touch-backend). It works for me.

For me, I was getting this error just for having breakpoints set in the callback on the drop. What is interesting is it would fail even after all breakpoints were continued and the browser wasn't paused. As soon as I removed the breakpoints, the error when away.

Im having the same issue. Replaced this package with react-dnd-touch-backend but that doesn't really help solving the issue. I would rather run html5. Tried setting keys on all elements that is being printed by an iterator.

I was getting the same error. I was re-applying a HOC on the draggable components on every render so they were always different which confused react-dnd.

@hakunin did you resolve this (I also only get this error after another runtime error has occurred).

I've since refactored all the DnD code I've got and I still get it when I start dragging. Now that I moved all the code into a single util file and made it configurable, I might be able to finally find the reason it happens at some point. Will post about it here when I do. (I am using TouchBackend in mouse setting btw)

Thanks for the response. I only get this error after some other error occurs while dragging -- at which point it seems unrecoverable. is that the same for you?

Mine complains every time I start dragging.

My case was that the component in endDrag(props, monitor, component) was undefined after the item was dropped. So that was causing my original error which led to the console stream of "Cannot call hover after drop" messages.

I've been able to resolve this issue thanks to this comment: https://github.com/react-dnd/react-dnd/issues/431#issuecomment-317219509

i met this when i caused other uncatched error before.there is no this error when there were no others errors.

In my case it happened when in endDrag i call some action/function that cause error. So basicaly uncatched error made dnd stuck. you can use try catch when calling some function/action in endDrag block.
endDrag: (props, monitor) => { try { handleEndDrag(); } catch(errror) { console.error(error)} }

A debugger statement inside the onDrop function was also the source of the error for me. Removing it makes the error disappear but then I am having a hard time debugging without it.

Any idea why debugger is triggering this error?

Not sure if it's of any interest but I am running an Electron app.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings