Cannot have two HTML5 backends at the same time
prakhar1989 opened this issue · comments
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?
Check out the source for DragDropContext. I think you should be able to reuse the existing manager if it is specified by a component above the tree. But it's still a tricky question.. Do you have any proposed solutions?
I think you should be able to reuse the existing manager if it is specified by a component above the tree.
Even if this is possible, the component that is exported should be able to work independently as well, and in cases where the backend exists (in the app where the component is being used) somewhere up the chain should be reused. I'll try to read the source and see if this can be pulled off.
Unfortunately, the only solution I can think of right now, is to export the final component as it is, and expect the user to add a DragDropContext
with a backend of their choosing. What do you think?
the component that is exported should be able to work independently as well, and in cases where the backend exists (in the app where the component is being used) somewhere up the chain should be reused.
Yes, this is possible by not using DragDropContext
altogether and instead manually using dragDropManager
in the context. Your component may look into context, and either pass the existing dragDropManager
down the context, or create its own. It seems somewhat fragile though.
Unfortunately, the only solution I can think of right now, is to export the final component as it is, and expect the user to add a DragDropContext with a backend of their choosing. What do you think?
I think this is the most flexible solution. You can also be opinionated there and export <MyComponentContext>
that applies DragDropContext(HTML5Backend)
.
export
<MyComponentContext>
that appliesDragDropContext(HTML5Backend)
I'm sorry, I don't quite understand this. Can you clarify this a bit more?
export default function MyTagControlContext(DecoratedClass) {
return DragDropContext(HTML5Backend)(DecoratedClass);
}
and you can tell users to either wrap their top-level component into MyTagControlContext
or use DragDropContext
directly if they already use React DnD.
Ah! How about this? Does this look too ugly?
// in main component file
module.exports = {
WithContext: DragDropContext(HTML5Backend)(ReactTags),
WithOutContext: ReactTags
};
The usage can then be something like
var ReactTags = require('react-tags').WithContext; // if your app doesn't use react-dnd
var ReactTags = require('react-tags').WithOutContext; // if your app already uses react-dnd.
I don't think this would work because each <ReactTags>
would get its own copy of backend and lead to the invariant error you pasted above because those backends handle the same global window events.
What I think will work is you can manually create dragDropManager
(just like DragDropContext
does internally) and use the same instance of it for all ReactTag
instances—with a fallback to the manager defined in context
.
I mean exporting something like this from your library:
let defaultManager;
function getDefaultManager() {
if (!defaultManager) {
defaultManager = new DragDropManager(HTML5Backend);
}
return defaultManager;
}
class ReactTagContext {
static contextTypes = {
dragDropManager: PropTypes.object.isRequired
};
static childContextTypes = {
dragDropManager: PropTypes.object.isRequired
};
getChildContext() {
return {
dragDropManager: this.context.dragDropManager || getDefaultManager()
};
}
render() {
return <ReactTag {...props} />
}
}
Thanks a lot, Dan! I'll try this out and get back to you. Thank you for sharing the code 😀
No prob.. If you do it like this, just export that class instead of exporting ReactTags
directly. It should be usable "as is", without any wrappers or decorators.
So Dan! For the heck of it, I was trying out the multiple exports solution above -
// in main component file
module.exports = {
WithContext: DragDropContext(HTML5Backend)(ReactTags),
WithOutContext: ReactTags
};
In my other app, I tried importing the component without the context and much to my delight, it seems to be working fine!
Do you think this is a hacky solution and I should go ahead with what you've proposed or should I let this be?
@prakhar1989 Are you sure this works with multiple <Tags />
on the page?
Hmm, then maybe it's fine ;-). Let me know if you have any issues with this approach!
Will do! Thanks a ton again for all your help.
PS: Do you have better naming ideas for WithContext
and WithoutContext
?
@prakhar1989 I'd probably just export WithContext
version directly, and put NoContext
as a static field on it.
I'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?
setup() {
...
// Events already setup - do nothing
if (this.constuctor.isSetUp) return;
// Don't throw an error, just return above.
//invariant(!this.constructor.isSetUp, 'Cannot have two HTML5 backends at the same time.');
this.constructor.isSetUp = true;
...
}
why not simply do this inside the HTML5Backend code
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?
Hi @gaearon - I am running into this issue as well except in my case, I have a page in which I've pieced together disparate react components within an angular template due to (angular) performance. What I have is a page which builds questions, choices, and more in a recursive tree structure. I also have a toolbar and a question library that use DnD to add things to the question tree. My problem is that I have now setup multiple react components that live within an angular context. Because of this, I am wrapping each of those with a DragDropContext
which causes this error. I tried following the above thread but it's not entirely clear to me what I could do for these separate react components to share a context without converting everything else on my page to be React (not ideal). I am a bit familiar with the ES6 syntax but I am working on a project that is still using ES5. Is there a way to apply the shared DragDropManager
concept from above? I've tried it so far and I don't seem to have access to DragDropManager
since it's in dnd-core
Thanks for your help and for this awesome library!
P.S. If it matters, I'm using ngReact.
@prakhar1989 @globexdesigns @gaearon
I am wondering the same thing why HTML5 backend can't just re-use the backend if multiple are used? Per my previous comment, this is really making react-dnd unusable for me as I have multiple react areas within an angular page that need to be able to DnD between each other and am hitting a wall with this.
Anyone have any quick fix for this? I'm at a dead stop in my development.
@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,
Thanks, @prakhar1989 ! But I'm not following how the multiple export with one wrapped with context and one not solves the issue. My problem is not being potentially embedded into another application with react-dnd but rather not being able to wrap my entire dnd-enabled area (with several react and angular components/directives) in react so I was trying to wrap the context around just those react components in my page that support DnD...I'd love to try @gaearon 's approach from above but I don't have access to the DragDropManager
in order to new one up...
I'm having the exact same issue. I strongly agree with @abobwhite that this makes components less reusable.
This thread led me to solving my invariant problem by moving the HTML5Backend
and DragDropContext
higher in my component hierarchy, just like the docs recommend.
This is a weird problem. I am working on a nested reorderable component and I have DragDropContext nested inside the parent one. It seems to work standalone (but it still has nested DragDropContext).
But when I use that component inside another project which has DragDropContext initialised above the hierarchy, I get this error.
I ran into this issue with an application I'm working on. I had full control over all the components so I ended up instead of using @DragDropContext(HTMLBackend)
I ended up using something very close to @gaearon code in this comment to create a decorator
that would give a shared drag drop context. It works really well.
I just had the same thing happen to me and the problem was that I had ComponentA which used @DragDropContext(HTMLBackend)
and then ComponentB which also had @DragDropContext(HTMLBackend)
.
ComponentB was imported in ComponentA which caused the error for me. All I had to do was to remove the DragDropContext from ComponentB and it worked.
What if there are 2 DrapDropContext
in the app but they are not parent and child?
My case:
- Intialize first dnd Context
- Initialize as sibbling a second dnd Context. 💣
I can't check on childContext because second component is not a child of first dnd component/context
To solve my problem I did a singleton with this code:
import { DragDropManager } from 'dnd-core';
import HTML5Backend from 'react-dnd-html5-backend';
let defaultManager;
/**
* This is singleton used to initialize only once dnd in our app.
* If you initialized dnd and then try to initialize another dnd
* context the app will break.
* Here is more info: https://github.com/gaearon/react-dnd/issues/186
*
* The solution is to call Dnd context from this singleton this way
* all dnd contexts in the app are the same.
*/
export default function getDndContext() {
if (defaultManager) return defaultManager;
defaultManager = new DragDropManager(HTML5Backend);
return defaultManager;
}
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:
import getDndContext from 'lib/dnd-global-context';
const ParentComponent = React.createClass({
childContextTypes: {
dragDropManager: React.PropTypes.object.isRequired,
},
getChildContext() {
return {
dragDropManager: getDndContext(),
};
},
render() {
return (<ChildComponentWithDndContext />);
},
I think the key is that I only initialize dnd
context once. What do you think?
@andresgutgon thanks. It works for me too
@andresgutgon you solution is great, but it's weird that DrapDropContext
doesn't destroy DragDropManager
on componentWillUnmount
, i.e. if you don't use 2 DrapDropContext
s at the same time, but rather on 2 different pages -- you still cannot use them. I'm going to try your hack in my case, but it's still 100% weird to even have hacks for such trivial situation.
Also any idea why having 2 DragDropManager
is something bad/wrong in react-dnd
?
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
import {DragDropContext} from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
export default DragDropContext(HTML5Backend);
components/MyComponent.js
import { Component } from 'react';
import withDragDropContext from '../lib/withDnDContext';
class MyComponent extends Component {
render() {
return (
<div>
// other children
</div>
);
}
export default withDragDropContext(MyComponent);
Hello, does anybody knows how to solve when you have one component wrapped in DragDropContext and then another component which uses DragDropContext as well, but this component (react big calendar) is an npm package, so remove it from there is not a solution and they are next to each other, not parent-child... you can have a look here https://github.com/martinnov92/TSCalendar (it is work in progress - so it is little bit messy :D) thank you
I believe I've exactly same issue with using react-mosaic tiling window manager which is using react-dnd and my own components, that are also using react-dnd.
So I'm guessing this in not solved directly in react-dnd, but there are some workarounds we can apply.
is there an example of a full working code that fixes this issue?
@gcorne's solution works
Hi i have same problem but we use react-data-grid and try to add react-big-calendar
After add component which include react-big-calendar we have error "Uncaught Error: Cannot have two HTML5 backends at the same time."
react-big-calendar use react-dnd and react-dnd-html5-backend how to solve that problem?
Hi @szopenkrk did you tried this #186 (comment) ?
But I think you would need to do a pull request to react-big-calendar to accept a Dnd backend
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):
import * as React from 'react';
import {DragDropContext} from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
// context singleton
let context: Function;
export function withDragDropContext<P>(
Component: React.ComponentClass<P> | React.StatelessComponent<P>,
): React.ComponentClass<P> {
// ensure a singleton instance of the context exists
if (!context) {
context = DragDropContext<P>(HTML5Backend);
}
return context(Component);
}
And then use it as follows in your components:
import * as React from 'react';
import {withDragDropContext} from 'components/WithDragDropContext';
class MyClass extends React.Component<IMyClassProps, {}> {
// ...
}
export default withDragDropContext<IMyClassProps>(MyClass);
N.B.
I've not tried yet but you should probably be able to populate the context variable during the declaration:
const context = DragDropContext(HTML5Backend);
and then skip the if (!context) {...
part.
@codeaid Thank you!
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.
Why didn't work @guang2013 any of the solutions here?
No, still no solutions, I don't know, I tried to drag the card into bigCalendar, and I made the card as dragSource and the cardsList as a DragAndDropContext, the calendar as another DragAndDropContext, then two html5backend errors throw out, I tried to use any method provided here but no one can solve my problem. @andresgutgon , when are you online, can I talk to you directly on this? Much appreciated.
@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.
@gcorne's solution worked for me to allow using react-dnd in an app with react-hot-loader. Still, I'm a little surprised it didn't work out of the box!
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.
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 DragDropContextProvider
rendering outside the app rerender cycle.
As in, don't directly do a ReactDOM.render
as a callback to the observe
function.
Instead do this:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { DragDropContextProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import './index.css';
import App from './App';
ReactDOM.render(
<DragDropContextProvider backend={HTML5Backend}>
<App />
</DragDropContextProvider>,
document.getElementById('root')
)
App.js
import React, { Component } from 'react';
import Board from './Board';
import { observe } from './Game';
class App extends Component {
state = {
knightPosition: [0, 0]
}
componentDidMount = () => {
observe(knightPosition => {
this.setState(prevState => ({
...prevState,
knightPosition
}));
});
}
render() {
return (
<div className="App">
<Board knightPosition={this.state.knightPosition} />
</div>
);
}
}
export default App;
I think this is related to this issue
react-tags/react-tags#497
@gaearon I know this is old, but how do I actually get the hold of DragDropManager
in your example? It's not exported anywhere.
defaultManager = new DragDropManager(HTML5Backend);
My problem is that I'm rendering a few widgets on a page via some 3rd party API, and I can't move DragDropContextProvider
higher than my actual widget.
Solved this issue by deleting the old build.
just delete the dist folder or edit index.html. I don't know the exact problem but this worked for me
Fortunately, I could find the (hidden) answer of @gcorne (#186 (comment)). 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 solution I figured out that works for me and allows me to use either HTML5 or Touch backend is:
Create a singleton HOC component:
import {DragDropContext} from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import TouchBackend from "react-dnd-touch-backend";
const DragAndDropHOC = props => {
return <React.Fragment>
{props.children}
</React.Fragment>
};
export default {
HTML5: DragDropContext(HTML5Backend)(DragAndDropHOC),
Touch: DragDropContext(TouchBackend)(DragAndDropHOC),
}
Then a provider component:
const DragDrop = props => {
if (props.isTouch) {
return <DragDropContext.Touch>{props.children}</DragDropContext.Touch>
} else {
return <DragDropContext.HTML5>{props.children}</DragDropContext.HTML5>
}
};
And use <DragDrop isTouch={props.isTouch} />
wherever I need it.
For developers who come across the same issue, you can take a look at this HOC solution
I am running into this problem now with Jest tests. HTML5-Backend is treated like a singleton across the Jest tests (which is why the problem is happening...i think)
Detailed problem in SO :
Using hooks
import { createDndContext } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
const manager = useRef(createDndContext(HTML5Backend));
return (
<DndProvider manager={manager.current.dragDropManager}>
....
</DndProvider>
)
A better solution using Hooks (thanks @jchonde):
import { DndProvider, createDndContext } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import React, { useRef } from "react";
const RNDContext = createDndContext(HTML5Backend);
function useDNDProviderElement(props) {
const manager = useRef(RNDContext);
if (!props.children) return null;
return <DndProvider manager={manager.current.dragDropManager}>{props.children}</DndProvider>;
}
export default function DragAndDrop(props) {
const DNDElement = useDNDProviderElement(props);
return <React.Fragment>{DNDElement}</React.Fragment>;
}
so then you can use elsewhere:
import DragAndDrop from "../some/path/DragAndDrop";
export default function MyComp(props){
return <DragAndDrop>....<DragAndDrop/>
}
My solution:
Make the children component don't import react-dnd
directly.
Pass the DragDropContext
and HTML5Backend
from parent to children component.
DragDropContextProvider标签里加一个不重复的key就解决了<DragDropContextProvider backend={HTML5Backend} key={Math. random()}></DragDropContextProvider>
In the typescript, i made the below component. (thanks @jchonde @ttessarolo )
import { DndProvider, createDndContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import React, { PropsWithChildren, useRef } from 'react';
const RNDContext = createDndContext(HTML5Backend);
function DragAndDrop({ children }: PropsWithChildren<{}>): JSX.Element {
const manager = useRef(RNDContext);
return <DndProvider manager={manager.current.dragDropManager}>{children}</DndProvider>;
}
export default DragAndDrop;
And used a component like this
function SomeComponent(): JSX.Element {
return (
<DragAndDrop>
...
</DragAndDrop>
);
}
DragDropContextProvider标签里加一个不重复的key就解决了
<DragDropContextProvider backend={HTML5Backend} key={Math. random()}></DragDropContextProvider>
good job!
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.
I 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 load react-dnd
itself. 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 rootElement
from it's default value, window
(see below).
I did the following:
....
const el = document.getElementById('my-widget-root-element')
<DndProvider backend={HTML5Backend} options={{ rootElement: el }}>
{props.children}
</DndProvider>
....
This makes it so that root.__isReactDndBackendSetUp
is false
when react-dnd
is instantiated in my widget because root
is my element, not window
.
react-dnd/packages/backend-html5/src/HTML5BackendImpl.ts
Lines 97 to 114 in 339dd7a
Through my inspection, this rootElement
option is not documented, though this page https://react-dnd.github.io/react-dnd/docs/api/dnd-provider states that the options
prop to DndProvider
is dependent on the backend in use. There is no mention of the options available to the HTML5Backend
here 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 the rootElement
option to only listen to the part of the DOM that they are associated with instead of the entire window
.
Hello, does anybody know how to solve when you have one component wrapped in DragDropContext and then another component which uses DragDropContext as well, but this component (react big calendar) is an npm package, so remove it from there is not a solution and they are next to each other, not parent-child...
Likewise to @mauron85 and @szopenkrk
In the typescript, i made the below component. (thanks @jchonde @ttessarolo )
import { DndProvider, createDndContext } from 'react-dnd'; import HTML5Backend from 'react-dnd-html5-backend'; import React, { PropsWithChildren, useRef } from 'react'; const RNDContext = createDndContext(HTML5Backend); function DragAndDrop({ children }: PropsWithChildren<{}>): JSX.Element { const manager = useRef(RNDContext); return <DndProvider manager={manager.current.dragDropManager}>{children}</DndProvider>; } export default DragAndDrop;And used a component like this
function SomeComponent(): JSX.Element { return ( <DragAndDrop> ... </DragAndDrop> ); }
DragDropContextProvider标签里加一个不重复的key就解决了
<DragDropContextProvider backend={HTML5Backend} key={Math. random()}></DragDropContextProvider>
using a combination of @milban and @MJGang's solution worked for me. the drag and drop singleton needed the random key generation key={Math. random()
to rid of the two HTML5 backend error
Related to: frontend-collective/react-sortable-tree#788
I 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"
createDndContext
was removed some time ago. The recommended method is to attach in a stable container component that contains your drag/drop components
In the typescript, i made the below component. (thanks @jchonde @ttessarolo )
import { DndProvider, createDndContext } from 'react-dnd'; import HTML5Backend from 'react-dnd-html5-backend'; import React, { PropsWithChildren, useRef } from 'react'; const RNDContext = createDndContext(HTML5Backend); function DragAndDrop({ children }: PropsWithChildren<{}>): JSX.Element { const manager = useRef(RNDContext); return <DndProvider manager={manager.current.dragDropManager}>{children}</DndProvider>; } export default DragAndDrop;And used a component like this
function SomeComponent(): JSX.Element { return ( <DragAndDrop> ... </DragAndDrop> ); }
This approach worked for me.
react-dnd version ^11.1.3
What is the modern approach to this?
Seems both createDndContext and DragDropContext are removed
The newer way is to use the react context API:
<DndProvider backend={HTML5Backend}>
{/* app */}
</DndProvider>
DndProvider
has an options
prop in where you can set rootElement
which 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 make HTML5Backend
singleton. I tried this approach with "react-dnd": "^14.0.2"
const myFirstId = 'first-DnD-Containier';
const mySecondId = 'second-DnD-Containier';
export const DndWrapper = React.memo((props) => {
const [context, setContext] = useState(null);
useEffect(() => {
setContext(document.getElementById(props.id))
},[props.id])
return context ? (
<DndProvider backend={HTML5Backend} options={{ rootElement: context}}>
{props.children}
</DndProvider>
) : null;
});
export default function App() {
return (
<div>
<div id={myFirstId}>
<DndWrapper id={myFirstId}>
<MyOtherComponents />
</DndWrapper>
</div>
<div id={mySecondId}>
<DndWrapper id={mySecondId}>
<MyOtherComponents />
</DndWrapper>
</div>
</div>
);
}
P.S. Make sure by the time you call document.getElementById
the DOM
exist with ids.
The recommended method is to attach in a stable container component that contains your drag/drop components
@darthtrevino Can you expand on this a little more? This seems like the solution to an error I'm having but it would help if I understood it more. Thank you.
DndProvider
has anoptions
prop in where you can setrootElement
which 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 makeHTML5Backend
singleton. I tried this approach with"react-dnd": "^14.0.2"
const myFirstId = 'first-DnD-Containier'; const mySecondId = 'second-DnD-Containier'; export const DndWrapper = React.memo((props) => { const [context, setContext] = useState(null); useEffect(() => { setContext(document.getElementById(props.id)) },[props.id]) return context ? ( <DndProvider backend={HTML5Backend} options={{ rootElement: context}}> {props.children} </DndProvider> ) : null; }); export default function App() { return ( <div> <div id={myFirstId}> <DndWrapper id={myFirstId}> <MyOtherComponents /> </DndWrapper> </div> <div id={mySecondId}> <DndWrapper id={mySecondId}> <MyOtherComponents /> </DndWrapper> </div> </div> ); }
P.S. Make sure by the time you call
document.getElementById
theDOM
exist with ids.
work for me, react-dnd@16.0.1
The newer way is to use the react context API:
<DndProvider backend={HTML5Backend}> {/* app */} </DndProvider>
it worked for me, thank you
DndProvider
has anoptions
prop in where you can setrootElement
which 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 makeHTML5Backend
singleton. I tried this approach with"react-dnd": "^14.0.2"
const myFirstId = 'first-DnD-Containier'; const mySecondId = 'second-DnD-Containier'; export const DndWrapper = React.memo((props) => { const [context, setContext] = useState(null); useEffect(() => { setContext(document.getElementById(props.id)) },[props.id]) return context ? ( <DndProvider backend={HTML5Backend} options={{ rootElement: context}}> {props.children} </DndProvider> ) : null; }); export default function App() { return ( <div> <div id={myFirstId}> <DndWrapper id={myFirstId}> <MyOtherComponents /> </DndWrapper> </div> <div id={mySecondId}> <DndWrapper id={mySecondId}> <MyOtherComponents /> </DndWrapper> </div> </div> ); }
P.S. Make sure by the time you call
document.getElementById
theDOM
exist with ids.
That works for me! Really save my ass!! react-dnd@14.0.3