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
:
test-app@0.1.0
├─┬ @package-name/react@1.0.0 -> ./../package-name-react
│ ├─┬ react-dom@17.0.2
│ │ └── react@17.0.2 deduped
│ └── react@17.0.2 // <----------------
├─┬ next@12.1.0
│ ├── react@17.0.2 deduped
│ ├─┬ styled-jsx@5.0.0
│ │ └── react@17.0.2 deduped
│ └─┬ use-subscription@1.5.1
│ └── react@17.0.2 deduped
├─┬ react-dom@17.0.2
│ └── react@17.0.2 deduped
└── react@17.0.2 // <----------------
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()],
},
]