I have an NPM package I am working on which has a dependency of react
. I then have a test app which has react
installed as a dependency. When I import my npm package into the test app, I get the following error:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
Running npm ls react
in my test app suggests I might have a duplicate of react
:
[email protected]
├─┬ @package-name/[email protected] -> ./../package-name-react
│ ├─┬ [email protected]
│ │ └── [email protected] deduped
│ └── [email protected] // <----------------
├─┬ [email protected]
│ ├── [email protected] deduped
│ ├─┬ [email protected]
│ │ └── [email protected] deduped
│ └─┬ [email protected]
│ └── [email protected] deduped
├─┬ [email protected]
│ └── [email protected] deduped
└── [email protected] // <----------------
My package.json for my npm package looks like this:
{
"name": "@package-name/react",
"version": "1.0.0",
"description": "",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"files": [
"dist"
],
"types": "dist/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"clean": "rimraf dist",
"build": "npm run clean && rollup -c"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.0.4",
"@rollup/plugin-typescript": "^8.2.5",
"rimraf": "^3.0.2",
"rollup": "^2.56.2",
"rollup-plugin-dts": "^3.0.2",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.1",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.3.5"
},
"dependencies": {
"socket.io-client": "^4.4.1"
},
"peerDependencies": {
"react": "17.0.2",
"react-dom": "17.0.2"
}
}
When I remove react
and react-dom
from peerDependencies
, the error goes away but causes other issues. It's almost like peerDependencies
are being installed and rolled up in my package.
My component in my package is very simple at this stage and is like so:
const MyComponent = ({
children
}) => {
const [myValue, setValue] = useState(false);
useEffect(() => {
setFlagValue(true)
}, []);
return (
<>
{children}
</>
)
};
I am then consuming this package in my test app like so:
import { MyComponent } from "package-name/react";
const MyApp = () => {
<MyComponent>
<div>Hello world</div>
</MyComponent>
}
Rollup config:
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import dts from 'rollup-plugin-dts';
const packageJson = require('./package.json');
export default [
{
input: 'src/index.ts',
output: [
{
file: packageJson.main,
format: 'cjs',
sourcemap: true,
name: 'react-ts-lib'
},
{
file: packageJson.module,
format: 'esm',
sourcemap: true
}
],
plugins: [
external(),
resolve(),
commonjs(),
typescript({ tsconfig: './tsconfig.json' }),
postcss(),
terser()
],
},
{
input: 'dist/esm/types/index.d.ts',
output: [{ file: 'dist/index.d.ts', format: "esm" }],
external: [/\.css$/],
plugins: [dts()],
},
]
CodePudding user response:
It was not clear from the question description, but looking at the repo, I see that the package is installed locally.
"dependencies": { "next": "12.1.0", "react": "17.0.2", "react-dom": "17.0.2", "react-ts-lib": "file:../react-ts-lib" },
Which means that the lib code still resolves react
using its own node_modules
(installed locally) rather than the app dependencies.
One way to fix this issue could be to setup the lib project with something like create-react-library, which addresses this problem explicitly:
If you use react-hooks in your project, when you debug your example you may run into an exception Invalid Hook Call Warning. This issue explains the reason, your lib and example use a different instance, one solution is rewrite the
react
path in your example [app]package.json
to'file:../node_modules/react'
or'link:../node_modules/react'
.
Note that in your case, you could simply change the path of react
in the lib's devDependencies
to point to the app's react
:
"devDependencies": {
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.0.4",
"@rollup/plugin-typescript": "^8.2.5",
"@types/react": "^17.0.18",
"react": "file:../my-app/node_modules/react",
Or the other way around, depending on what makes the most sense.
There are also other ways to do local development of a React library:
- A yarn monorepo makes this really easier to deal with by lifting up dependencies to the root.
- How to link a local react library with a local react project during development?
- Publish your library to either github or the npm repository in order to install it in your app.
This looks like the same tutorial you used to scaffold your project.