React-dnd: Warning With Nested DragSource

Created on 6 Apr 2016  ·  27Comments  ·  Source: react-dnd/react-dnd

If you have a DragSource inside of a component that is also a DragSource (i.e. drag something within a draggable), whenever the parent component is dragged, the following warning appears.

Warning: setState(...): Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
warning @   modules.js?hash=107dc56…:19491
getInternalInstanceReadyForUpdate   @   modules.js?hash=107dc56…:5476
ReactUpdateQueue.enqueueSetState    @   modules.js?hash=107dc56…:5617
ReactComponent.setState @   modules.js?hash=107dc56…:15132
handleChange    @   modules.js?hash=107dc56…:20844
handleChange    @   modules.js?hash=107dc56…:22861
dispatch    @   modules.js?hash=107dc56…:23568
addSource   @   modules.js?hash=107dc56…:23124
registerSource  @   modules.js?hash=107dc56…:21172
receiveType @   modules.js?hash=107dc56…:21099
receiveProps    @   modules.js?hash=107dc56…:21089
DragSource(NestedComponent) @   modules.js?hash=107dc56…:21062
ReactCompositeComponentMixin.mountComponent @   modules.js?hash=107dc56…:6679
ReactCompositeComponent_mountComponent  @   modules.js?hash=107dc56…:896
ReactReconciler.mountComponent  @   modules.js?hash=107dc56…:5167
ReactMultiChild.Mixin.mountChildren @   modules.js?hash=107dc56…:13562
ReactDOMComponent.Mixin._createContentMarkup    @   modules.js?hash=107dc56…:10901
ReactDOMComponent.Mixin.mountComponent  @   modules.js?hash=107dc56…:10789
ReactReconciler.mountComponent  @   modules.js?hash=107dc56…:5167
ReactCompositeComponentMixin._updateRenderedComponent   @   modules.js?hash=107dc56…:7101
ReactCompositeComponentMixin._performComponentUpdate    @   modules.js?hash=107dc56…:7075
ReactCompositeComponentMixin.updateComponent    @   modules.js?hash=107dc56…:7004
ReactCompositeComponent_updateComponent @   modules.js?hash=107dc56…:896
ReactCompositeComponentMixin.receiveComponent   @   modules.js?hash=107dc56…:6936
ReactReconciler.receiveComponent    @   modules.js?hash=107dc56…:5217
ReactCompositeComponentMixin._updateRenderedComponent   @   modules.js?hash=107dc56…:7093
ReactCompositeComponentMixin._performComponentUpdate    @   modules.js?hash=107dc56…:7075
ReactCompositeComponentMixin.updateComponent    @   modules.js?hash=107dc56…:7004
ReactCompositeComponent_updateComponent @   modules.js?hash=107dc56…:896
ReactCompositeComponentMixin.receiveComponent   @   modules.js?hash=107dc56…:6936
ReactReconciler.receiveComponent    @   modules.js?hash=107dc56…:5217
ReactCompositeComponentMixin._updateRenderedComponent   @   modules.js?hash=107dc56…:7093
ReactCompositeComponentMixin._performComponentUpdate    @   modules.js?hash=107dc56…:7075
ReactCompositeComponentMixin.updateComponent    @   modules.js?hash=107dc56…:7004
ReactCompositeComponent_updateComponent @   modules.js?hash=107dc56…:896
ReactCompositeComponentMixin.performUpdateIfNecessary   @   modules.js?hash=107dc56…:6952
ReactReconciler.performUpdateIfNecessary    @   modules.js?hash=107dc56…:5232
runBatchedUpdates   @   modules.js?hash=107dc56…:5832
Mixin.perform   @   modules.js?hash=107dc56…:6304
Mixin.perform   @   modules.js?hash=107dc56…:6304
assign.perform  @   modules.js?hash=107dc56…:5789
flushBatchedUpdates @   modules.js?hash=107dc56…:5850
ReactUpdates_flushBatchedUpdates    @   modules.js?hash=107dc56…:896
Mixin.closeAll  @   modules.js?hash=107dc56…:6370
Mixin.perform   @   modules.js?hash=107dc56…:6317
ReactDefaultBatchingStrategy.batchedUpdates @   modules.js?hash=107dc56…:10295
enqueueUpdate   @   modules.js?hash=107dc56…:5879
enqueueUpdate   @   modules.js?hash=107dc56…:5460
ReactUpdateQueue.enqueueSetState    @   modules.js?hash=107dc56…:5626
ReactComponent.setState @   modules.js?hash=107dc56…:15132
handleChange    @   modules.js?hash=107dc56…:21121
handleChange    @   modules.js?hash=107dc56…:22861
dispatch    @   modules.js?hash=107dc56…:23568
(anonymous function)    @   modules.js?hash=107dc56…:22100
endDragIfSourceWasRemovedFromDOM    @   modules.js?hash=107dc56…:29563
handleTopDrop   @   modules.js?hash=107dc56…:29860

An example of this would be:

class ParentDraggable extends React.Component {
    render() {
        return this.props.connectDragSource(
            <NestedDraggable/>
        );
    }
}

//... Skip the code for containers

class NestedDraggable extends React.Component {
    render() {
        return this.props.connectDragSource(
            <div></div>
        );
    }
}

I believe the issue stems from the fact that rerendering the parent component causes the child DragSource call to make a call to setState during receiveProps function call?

triage wontfix

Most helpful comment

Just in case it helps someone: I encountered this when I was rendering a component that was a drag source in my custom drag layer because it was convenient. I used the drag source component because it was the same entity that I was trying to drag, so it just made sense to me to keep the code "DRY" by reusing it. I didn't think about the fact that it had all the dragSource code in it - looking back, _of course_ I wouldn't want a drag source AS the thing to render in the custom drag layer.

I ultimately split up my component into a "Draggable" version that wrapped the base component, and then used the base component in my custom drag layer. No more warning, better code! 👍

All 27 comments

:+1:

Can you please provide a project reproducing the problem?

I'm trying to produce an simple example and am having problems reproducing outside of my private project that I'm working on. @nihtalak , do you have a repo on Github that produces the issue?

Here's a tiny repo exhibiting the described behavior: https://github.com/ebakan/react-dnd-nested-bug

Curiously, the Component only throws this error when it's listening for monitor.getItem(). If it's listening for monitor.isDragging() it'll work properly, as shown in this branch: https://github.com/ebakan/react-dnd-nested-bug/tree/no-error-with-is-dragging

It will also throw the error when listening to monitor.getItemType(): https://github.com/ebakan/react-dnd-nested-bug/tree/error-with-get-item-type

@ebakan - interesting. I'm wondering what's causing this bug to appear in my code, which does use isDragging. I am doing some wonky things with the nested draggable, so it could be a manifestation of the same bug you are seeing. Thanks for the help reproducing!

I have nested draggables too, but not sure if thats the issue.
It seems to be creating a new store and the code thats listening to. I made a video if that helps:
http://screencast.com/t/qCrJRPAS2MYR

Update: found the issue, I had a leftover listener so in my case it wasn't due to react-dnd.
Update2: I spoke too soon. Upon removing the leftover listener, I still get the warning.

I just added Dnd to another component and noticed this issue even without nested or multiple dragging components.

After a bit of digging I found that the issue is connected to custom drag layer rendering the component while being dragged.

If I put text instead the component being dragged it works fine. The component itself is the drag source.

@jchristman @hakunin I don't have nested draggables, but encountered Cannot update during an existing state transition when I had a drop target conditionally rendered based on isDragging. Could this be the case in any of your examples? Something sort of like this:

render() {
    var dropZone;
    if (this.props.isDragging) dropZone = <MyDropTarget />;
    return <div>
       <MyDragSource />
       {dropZone}
    </div>
}

Ended up working around it by using display: none/block to hide the target instead of adding and removing the component when dragging is active.

I think that could be the issue. I will check and let you know. That's is definitely the construct I'm using right now.

Naw - I'm getting the issue when the "drop" happens. Then it tries to rerender the component and throws this warning.

Got same warning when my drag preview was a component which was a DragSource (which I'm not supposed to do).

@jchristman did you ever come up with a solution for this?

Im working with nested dragSources and suddenly started seeing this error when dropping the nested resource.

I can provide an example but wanted to see if anyone had solved the issue, or had a suitable workaround.

Got same warning when my drag preview was a component which was a DragSource (which I'm not supposed to do).

Thanks @arjunu my warning was caused by this!
I still do have these occasionally so I guess some other reason is there to be fixed as well. Might be on my part :)

@gharwood1, I'm just livin with the issue until it can be fixed. I spent a few hours once trying to really understand this library so I could pull request a fix but it's pretty complex, so I moved on. I just don't have the time... :-(

@gaearon I don't quite have a project made that's capable of someone pulling down, but I think I can get one made if needed.

Basically, is what I'm trying to do here valid:

https://github.com/arackaf/booklist/blob/react-dnd-bug-freeze/react-redux/modules/subjects/components/subjectsList-es6.js

If I disable the conditional in line 64 everything's fine

if (this.props.draggingSubject){
    effectiveChildren.push(this.props.draggingSubject);
}

but the minute the results of the drag event starts modifying props and rendering of components, the warning above starts getting spit out frequently.

Should I not be doing this?

The use case is basically to "preview" the currently dragged item in the drop target. The above is a proof of concept - not wired into the Redux store yet, since I'm not sure if this is even valid.


Hm, looks like it's fine if I keep my changes local to SubjectDisplayContent, but the warnings start happening if the changes extend to SubjectDisplay. So basically, if a dropTarget sends props down that modify a different dropTarget, react-dnd gets annoyed. Is this expected, and I need to re-think some things, or is this a bug with dnd?

I'm experiencing exactly the same problem. What you can do in some situations is create a DragDropContext around each nested draggable. But this doesn't work as soon as you want a custom DragLayer..

I got the same error, except I am doing a nested dropTargets.
After looking around in the documentation, I tried using

isOver(options): Returns true if there is a drag operation is in progress, and the pointer is currently hovering over the owner. You may optionally pass { shallow: true } to strictly check whether only the owner is being hovered, as opposed to a nested target.

(with the shallow is true option) in the canDrop function of the parent dropTarget, and the issue went away. I would think the same could be applied to some other scenarios.

@jchenjc I tried this but it didn't work for me. To be honest my situation is extremely complicated with nested drag preview + drag source + drop target that needs to work across lists.

FWIW: I have both nested drag sources and a custom drag layer. When I remove the custom drag layer, the message goes away. From inspecting the stack trace, it looks like the problem is here:

printWarning    @   warning.js?8a56:36
warning @   warning.js?8a56:60
getInternalInstanceReadyForUpdate   @   ReactUpdateQueue.js?6531:54
enqueueSetState @   ReactUpdateQueue.js?6531:200
ReactComponent.setState @   ReactComponent.js?702a:63
handleChange    @   DragLayer.js?1cbc:124 <--------------
handleChange    @   DragDropMonitor.js?0588:60

Relevant code is here. I'm going to look into why the state might be different in my case to trigger a re-render...

With a dragsource+droptarget nested inside a droptarget, I get this issue.

Part of the reason this happened is because gaearon envisoned doing list drag and drop by making every list item a drop target, rather than making the list component a single drop target that determines what to do based upon the drag hover position. The latter way is how most other DnD systems I've seen do things.

Just in case it helps someone: I encountered this when I was rendering a component that was a drag source in my custom drag layer because it was convenient. I used the drag source component because it was the same entity that I was trying to drag, so it just made sense to me to keep the code "DRY" by reusing it. I didn't think about the fact that it had all the dragSource code in it - looking back, _of course_ I wouldn't want a drag source AS the thing to render in the custom drag layer.

I ultimately split up my component into a "Draggable" version that wrapped the base component, and then used the base component in my custom drag layer. No more warning, better code! 👍

@davidjoy woah, thanks for sharing! I am doing the same thing, need to try it as well!

@davidjoy Just fixed mine as well. Your suggestion points to not using DragSource and such for the custom layer. What I did was simply to use the naked component without DragSource in my custom drag layer and that did the trick.

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