I have a problem with understanding how my application that consumes components from a custom component library produces this error (coming from react-dnd
):
Invariant Violation: Expected drag drop context
Of course I get that it cannot find the context value that is necessary. However, it seems I am not able to provide a proper context value.
Assume my library looks as follow:
// src/DndComponent.tsx
import * as ReactDnd from "react-dnd";
const DndComponent = () => {
const [, drag] = ReactDnd.useDrag(
() => ({
type: "some-type",
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
}),
[]
);
return <div ref={drag}>Drag me</div>;
};
export default DndComponent;
// index.ts
export { default as DndComponent } from "./DndComponent";
// package.json
{
"name": "ui-lib",
"version": "0.0.1",
"type": "module",
"files": [
"dist"
],
"main": "./dist/index.umd.cjs",
"module": "./dist/index.js",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"peerDependencies": {
"react": "17.0.2",
"react-dnd": "14.0.5",
"react-dnd-html5-backend": "14.0.2"
},
"devDependencies": {
"@types/node": "18.15.11",
"@types/react": "17.0.45",
"@vitejs/plugin-react": "3.1.0",
"@vitejs/plugin-react-swc": "3.2.0",
"typescript": "4.9.3",
"vite": "4.1.4",
"vite-plugin-svgr": "2.4.0"
},
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.umd.cjs"
}
}
}
// vite.config.ts
import { defineConfig } from "vite";
import { resolve } from "path";
import react from "@vitejs/plugin-react-swc";
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, "src/index.ts"),
name: "index",
fileName: "index",
},
rollupOptions: {
external: ["react", "react-dnd", "react-dnd-html5-backend"],
output: {
globals: {
react: "react",
"react-dnd": "react-dnd",
"react-dnd-html5-backend": "react-dnd-html5-backend",
},
},
},
},
plugins: [react()],
});
And my application looks like this:
// App.tsx
import React from "react";
import * as ReactDnd from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { DndComponent } from "ui-lib";
function App() {
return (
<ReactDnd.DndProvider backend={HTML5Backend}>
<DndComponent />
</ReactDnd.DndProvider>
);
}
export default App;
// package.json
{
"name": "app-dir",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "npm run build && vite preview"
},
"dependencies": {
"react": "17.0.2",
"react-dnd": "14.0.5",
"react-dnd-html5-backend": "14.0.2",
"react-dom": "17.0.2",
"ui-lib": "file:../ui-lib"
},
"devDependencies": {
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@vitejs/plugin-react": "3.1.0",
"typescript": "4.9.3",
"vite": "4.1.4"
}
}
As you can see, I am wrapping DndComponent
inside ReactDnd.DndProvider
which should provide the context value.
If I re-export DndProvider
and HTML5Backend
from ui-lib
and consume these instead of directly from react-dnd
and react-dnd-html5-backend
, I don't have any issues and everything seems to work as expected:
// index.ts (ui-lib)
export { default as DndComponent } from "./DndComponent";
export { DndProvider } from "react-dnd"; // re-export from 'react-dnd' inside `ui-lib`
export { HTML5Backend } from "react-dnd-html5-backend"; // re-export from 'react-dnd-html5-backend' inside `ui-lib`
// App.tsx (app)
import React from "react";
import { DndComponent, DndProvider, HTML5Backend } from "ui-lib"; // consume from `ui-lib`
function App() {
return (
<DndProvider backend={HTML5Backend}>
<DndComponent />
</DndProvider>
);
}
export default App;
I found How to share context between different component libraries in react?, React Context API not working from custom NPM component library or How to share context between multiple libraries? that seem to have a very similar problem.
But as you can see, I already added react-dnd
and react-dnd- html5-backend
as peerDependencies
and added both to the Rollup config's external
property as well.