react-dnd: Cannot have two HTML5 backends at the same time
Hi Dan,
Just a quick one - I’m trying to use my own component that has react-dnd as a dependency in another app which itself uses react-dnd so the error above expected. In this case, what would be the best way to fix this?
Since the other component is my own, I can remove the DragDropContext call from while exporting the component but then that sacrifices the re-usability of the component. What do you advise?
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Comments: 75 (13 by maintainers)
Commits related to this issue
- use fix suggested here: https://github.com/react-dnd/react-dnd/issues/186#issuecomment-232382782 — committed to PieElements/corespring-placement-ordering by edeustace 7 years ago
Another approach that is a bit cleaner is to create a module that generates the decorator for a particular backend, and then use the decorator where needed:
lib/withDragDropContext.js
components/MyComponent.js
To solve my problem I did a singleton with this code:
And then in all the components that have a child that had
DragDropContext(HTML5Backend)I remove it from those child and in their parents I do this:I think the key is that I only initialize
dndcontext once. What do you think?Old issue, but in case somebody else ends up here from Google:
I had only 1 DND provider on my page, I was not integrating any other library that has DND, but I still ended up running into this error, somewhat randomly.
My problem was that the DragDropContextProvider was inside ReactRouter’s BrowserRouter element, which made the HTML5Backend to be reconstructed on every navigation, and if both the old page (that was navigated away from) and the new one (the one navigated to) had DND elements, the above error would occur.
The solution was to move the DragDropContextProvider out of BrowserRouter.
A better solution using Hooks (thanks @jchonde):
so then you can use elsewhere:
Probably a bit too late but I’ve come up with a similar but slightly different solution. I implemented a higher order component and simply use it in all my DragDropContext aware components.
Code looks like this (TypeScript):
And then use it as follows in your components:
N.B. I’ve not tried yet but you should probably be able to populate the context variable during the declaration:
and then skip the
if (!context) {...part.DragDropContextProvider标签里加一个不重复的key就解决了
<DragDropContextProvider backend={HTML5Backend} key={Math. random()}></DragDropContextProvider>Using hooks
This is a great point. If the component is able to detect multiple backends can’t it directly have the logic of falling back to the existing backend in scope?
In the typescript, i made the below component. (thanks @jchonde @ttessarolo )
And used a component like this
DndProviderhas anoptionsprop in where you can setrootElementwhich bounds DnD to that specified context, and unfortunately it isn’t documented well. This approach solved all my issues, as I had other component which was using DnD and they were out of my boundary and I wasn’t able to makeHTML5Backendsingleton. I tried this approach with"react-dnd": "^14.0.2"P.S. Make sure by the time you call
document.getElementByIdtheDOMexist with ids.and you can tell users to either wrap their top-level component into
MyTagControlContextor useDragDropContextdirectly if they already use React DnD.This thread led me to solving my invariant problem by moving the
HTML5BackendandDragDropContexthigher in my component hierarchy, just like the docs recommend.I mean exporting something like this from your library:
This one is for those mortals who tried out ReactDnD for the first time, followed the tutorial and ended up with a 64 square chessboard. I could drag my knight around as I pleased. Problem was when I tried to drop it in one of the
BoardSquare, it threw up with the 2 HTML5 backend issue.The Fix
As others have mentioned in comments before, move the
DragDropContextProviderrendering outside the app rerender cycle. As in, don’t directly do aReactDOM.renderas a callback to theobservefunction. Instead do this:index.jsApp.jsI’m running into a similar issue and I’d like to better understand why this limitation exists in the first place because it’s making writing re-usable components quite difficult. As it is, each component that uses react-dnd needs to be aware of various contexts that may exist in the application and deal with them accordingly. It would be preferable if each component could manage its own drag behaviour/context regardless of what else may be going on in the rest of the application.
For example, I may want to have an application screen which has several File Upload components, a sortable menu and a game with draggable elements. Each of those components have very different ways of dealing with drag events, and should really be in charge of their own context.
My first question, is why not simply do this inside the HTML5Backend code?
What is the modern approach to this?
Seems both createDndContext and DragDropContext are removed
I tried some of the suggestion above but get the following error: Module ‘“…/node_modules/react-dnd/dist/types”’ has no exported member ‘createDndContext’.t Version: “react-dnd”: “^13.1.1”, “grep -R -i createDndContext .” does not find any such string in the source files.
@gaearon I know this is old, but how do I actually get the hold of
DragDropManagerin your example? It’s not exported anywhere.My problem is that I’m rendering a few widgets on a page via some 3rd party API, and I can’t move
DragDropContextProviderhigher than my actual widget.createDndContextwas removed some time ago. The recommended method is to attach <DndProvider> in a stable container component that contains your drag/drop componentsI tried lots of the suggestions above but for my use case none of them worked for me. I have a chrome extension which adds my widget directly into the DOM of webpages. This error occurs when the webpage uses
react-dnd(for example Discord) and my widget tries to loadreact-dnditself. I can’t use the options above because I wouldn’t be able to control the webpage’s DndContext singleton.The error can be solved by changing
rootElementfrom it’s default value,window(see below). https://github.com/react-dnd/react-dnd/blob/339dd7aade81477af46becf4a67a2f65d6cfeb74/packages/backend-html5/src/OptionsReader.ts#L36I did the following:
This makes it so that
root.__isReactDndBackendSetUpisfalsewhenreact-dndis instantiated in my widget becauserootis my element, notwindow.https://github.com/react-dnd/react-dnd/blob/339dd7aade81477af46becf4a67a2f65d6cfeb74/packages/backend-html5/src/HTML5BackendImpl.ts#L97-L114
Through my inspection, this
rootElementoption is not documented, though this page https://react-dnd.github.io/react-dnd/docs/api/dnd-provider states that theoptionsprop toDndProvideris dependent on the backend in use. There is no mention of the options available to theHTML5Backendhere https://react-dnd.github.io/react-dnd/docs/backends/html5@martinnov92 I think this approach solves the problem you mention where two codebases are both using
reactDnd. If possible, they should both update therootElementoption to only listen to the part of the DOM that they are associated with instead of the entirewindow.Likewise to @mauron85 and @szopenkrk
Fortunately, I could find the (hidden) answer of @gcorne (https://github.com/react-dnd/react-dnd/issues/186#issuecomment-282789420). It solved my problem - which was assumed to be tricky - instantaneously. @prakhar1989 I have the feeling it is a real party answer and that it should be highlighted somehow, so maybe link it in the bug description?
The newer way is to use the react context API:
The solution I figured out that works for me and allows me to use either HTML5 or Touch backend is:
Create a singleton HOC component:
Then a provider component:
And use
<DragDrop isTouch={props.isTouch} />wherever I need it.@abobwhite This is how I’ve solved it. It’s definitely not a great solution but it seems to work as of now.
Hope this helps,
This approach worked for me. react-dnd version
^11.1.3I tried everything from above comment but typescript throwing below error: Module ‘“react-dnd”’ has no exported member ‘createDndContext’. Version: “react-dnd”: “14.0.0”, “react-dnd-html5-backend”: “14.0.0”
@guang2013 if you are using a library that depends on DragDropContext from react-dnd, this technique won’t work because the library won’t use your unified dndContext. Some libraries like react-sortable-tree will allow you to use the components without a context so you can wrap them yourself though.
I met the same situation as @andresgutgon , but could not solve with his method and all the method here I tried and no one works, is there anybody can help me? Thank you.
What if there are 2
DrapDropContextin the app but they are not parent and child?My case:
I can’t check on childContext because second component is not a child of first dnd component/context