You want to do the following:
- Put some parts of your app into a library (containing React components).
- Already test your library with your app before publishing to NPM.
So you use npm link
inside your library folder, and then npm link mylib
in your app folder.
This creates a symlink in app/node_modules/mylib
to /path/to/mylib
.
In the mylib
folder there is a node_moduls
, too, of course.
And it includes react
.
After all, you also want to test the library in isolation and for that you need React.
With the above setup, react
exists twice: app/node_modules/react
and app/node_modules/mylib/node_modules/react
.
As app/node_modules/mylib
is a symlink, NPM's normal deduplication fails here.
The result: React is added to the output bundle twice.
This is a problem for React hooks, even if it's the exact same React version!
You will see this 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
For one, react
should not be in the dependencies
, but in the peerDependencies
of your library.
And optionally in devDependencies
.
This is the solution you commonly find on the internet.
You remove the mylib/node_modules/react
folder and then instead do ln -s app/node_modules/react mylib/node_modules/react
.
That way the dependency gets deduplicated correctly.
The problem is that this is super annoying and you have to constantly adjust this.
If you want to test the library in another app, you have to adjust;
if you do some other npm install
, you might have to recreate the links.
If your app uses webpack, you can add the following to the config:
resolve: {
alias: {
"react": path.join(__dirname, "node_modules/react"),
},
},
Adding this alias takes precedence over the normal module resolution. So this might lead to some weird dependency problems down the line. Just be aware of that!
With that disclaimer out of the way: it works and I think it's save to just commit it to the apps repository.
- npm/npm#7742 (comment)
- https://gaiety.life/posts/react-peer-dependencies/
- https://legacy.reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react
- facebook/react#13991
- facebook/react#14721
- https://stackoverflow.com/questions/56021112/react-hooks-in-react-library-giving-invalid-hook-call-error
- https://iws.io/2022/invalid-hook-multiple-react-instances