react-dnd icon indicating copy to clipboard operation
react-dnd copied to clipboard

DragSource attempting to setup HTML5Backend a second time in v9.4.0

Open pjgates opened this issue 6 years ago • 8 comments

Describe the bug After updating to v9.4.0 we found that when mounting components using the DragSource decorator, it would sometimes attempt to setup the HTML5Backend again, thus triggering the error Uncaught Error: Cannot have two HTML5 backends at the same time (as this.window.__isReactDndBackendSetUp was not false).

The Dnd Context Provider is declared in the root of the page (and thus is above the components that are unmounted and mounted in the tree).

I suspect that either the teardown isn't triggering when it should, or the setup is triggering when it shouldn't, thus triggering this error.

Also, it should be noted that having multiple DragSources (such as items in a list), doesn't trigger this when they're rendered together. It's only when the list is replaced with another list that this bug is triggered.

Expected behaviour We expect that unmounting and mounting two components that use react-dnd to correctly update the this.window.__isReactDndBackendSetUp global variable without throwing an error.

Desktop (please complete the following information):

  • OS: macOS (although this has been reported on other systems as well)
  • Browser: Chrome
  • Version: 77

Additional context The stack trace from console:

Uncaught Error: Cannot have two HTML5 backends at the same time.
    at HTML5Backend.setup (HTML5Backend.js:388)
    at DragDropManagerImpl.handleRefCountChange (DragDropManagerImpl.js:48)
    at Object.dispatch (redux.js:227)
    at HandlerRegistryImpl.addSource (HandlerRegistryImpl.js:100)
    at registerSource (registration.js:13)
    at DragDropContainer.receiveType (decorateHandler.js:154)
    at DragDropContainer.receiveProps (decorateHandler.js:139)
    at DragDropContainer.componentDidMount (decorateHandler.js:115)
    at commitLifeCycles (react-dom.development.js:20049)
    at commitLayoutEffects (react-dom.development.js:22813)

pjgates avatar Oct 01 '19 01:10 pjgates

I am also having same issue. https://github.com/react-dnd/react-dnd/issues/1571

Enigma10 avatar Oct 14 '19 05:10 Enigma10

I am running into this type of issue as well, in my case I have some set of components mounted that uses react-dnd, and then say I open a modal with another type of component that also uses react-dnd and is wrapped with its own DndProvider, I encounter this error. Without wrapping the component I encounter other errors such as invariant found, etc. where it can't set up the drag/drop context.

On react-dnd and react-dnd-html5-backend versions 9.5.1

ozzyogkush avatar Dec 09 '19 16:12 ozzyogkush

@ozzyogkush Maybe you can try using a single DndProvider mounted at the root of your app - there should only be one per document.

darthtrevino avatar Dec 10 '19 20:12 darthtrevino

I found a temp. solution that works as such:

in my app index file:

const renderApp = () => {
    ReactDOM.render(
        <DndProvider backend={HTML5Backend}>
          <App />
        </DndProvider>,
        document.getElementById('app-wrapper')
    );
}

futher down the component tree...

<DndContext.Consumer>
  {({ dragDropManager }) => {
    return <MyComponent dragDropManager={dragDropManager} />;
  }}
</DndContext.Consumer>

In my component defined in an external package:

function DndConsumerWithFallback({ dragDropManager, children }) {
    return dragDropManager === undefined ?
        <DndProvider backend={HTML5Backend}>{children}</DndProvider> :
        <DndProvider manager={dragDropManager}>{children}</DndProvider>;
}

function MyComponent({ dragDropManager, ...componentProps }) {
    return (
        <DndConsumerWithFallback dragDropManager={dragDropManager}>
          <SomeDragAndDroppableStuff {...componentProps} />
        </DndConsumerWithFallback>
    );
}

This way, if the app doesn't provide the manager, it falls back on a default.

ozzyogkush avatar Dec 10 '19 20:12 ozzyogkush

@darthtrevino is the expectation that once the dnd provider component is mounted, it should never unmount ? (i.e. can it mount and unmount and re-mount ?)

Madhu94 avatar Mar 13 '20 13:03 Madhu94

@pjgates I don't know how bad this idea is but you can make it work by setting yourself the property to false on mount.

useLayoutEffect(() => {
   window.__isReactDndBackendSetUp = false;
}, [])

I don't have much knowledge about working of react dnd and I would like to get feedback on this approach wether this will break code or not.

RavenColEvol avatar Aug 09 '21 20:08 RavenColEvol

The DndProvider break down the HTML5Backend when it is unmounted, and the HTML5Backend will clear that variable in the window.

That being said, all of the testing of this library has been done with a stable root DndProvider in the sample apps

darthtrevino avatar Aug 09 '21 20:08 darthtrevino

@pjgates我不知道这个想法有多糟糕,但您可以通过在挂载时将属性设置为 false 来使其工作。

useLayoutEffect(() => {
   window.__isReactDndBackendSetUp = false;
}, [])

我对 react dnd 的工作知之甚少,我想获得有关这种方法的反馈,无论这是否会破坏代码。

11.1.3 我临时选择了这种方法,期待更好的办法,可能需要用升级版本才可以?

FontEndArt avatar May 19 '22 01:05 FontEndArt

[HTML5BackendImpl.ts#L108-L108](https://github.com/react-dnd/react-dnd/blob/8e6f62e612c0b0a376ac4a86cd371588dfcd2a70/packages/backend-html5/src/HTML5BackendImpl.ts#L101-L112)

maybe this rootElement can be replace by ReactRef? I cant use custom rootElement when my component is not mounted.

@darthtrevino

MeetzhDing avatar Oct 09 '22 04:10 MeetzhDing