66 lines
3.9 KiB
Markdown
66 lines
3.9 KiB
Markdown
---
|
|
title: 'React Library and Peer Dependency Woes'
|
|
date: 2021-06-22
|
|
tags:
|
|
- React
|
|
- Tech
|
|
- NPM
|
|
- OSS
|
|
description: >-
|
|
Building and consuming React libraries have a less than optimally documented trouble... peer dependencies. Let's discuss how to solve that!
|
|
---
|
|
|
|
You've been building your React component library, perhaps a UI Design System or a useful component you wish to share with the world. Your tests pass and it works great! Then, _you try consuming your component in another React app and suddenly you're met with the following error_:
|
|
|
|
> 
|
|
|
|
Worse yet is the [suggested documentation page](https://reactjs.org/warnings/invalid-hook-call-warning.html) suggests three very different possible reasons this error could occur. I'm here to say that so long as you're using hooks appropriately, the trouble lies in React peer dependencies resulting in you unknowingly having multiple versions of React.
|
|
|
|
## How to fix this
|
|
|
|
Whether you've set up your React library with [create-react-library](https://www.npmjs.com/package/create-react-library) or [create-react-app](https://www.npmjs.com/package/create-react-app) the first thing to check is your own `./package.json`. If you see `react` or `react-dom` inside of your `dependencies` or `devDependencies` then you've already found the issue!
|
|
|
|
> Ensure `react` and `react-dom` only exist within a `peerDependnecies` block.
|
|
|
|
You'll also want to manually remove them from the `./node_modules` directory in case they exist already.
|
|
|
|
```sh
|
|
rm -rf node_modules/react node_modules/react-dom
|
|
```
|
|
|
|
At this point your issue is likely solved in the consuming app.
|
|
|
|
### Fixing the fix... running tests and developing the addon locally
|
|
|
|
Okay great, if this works then you'll soon realize that you're unable to run tests or otherwise develop your addon locally at all because you need `react` and `react-dom`. The unfortunate reality is that you'll need to be intentional about when you install peer dependencies depending on what you're doing.
|
|
|
|
```sh
|
|
npm i --no-save react react-dom # this will not affect your package.json, and they may be auto-removed on the next npm install or yarn run
|
|
```
|
|
|
|
If you're finding this annoying, you may opt to add some scripts to your `./package.json` to make installing and removing peer dependencies really easy. You may also then call them as prerequesit commands to your other standard commands.
|
|
|
|
```json
|
|
{
|
|
scripts: {
|
|
"peers:install": "npm i --no-save react react-dom",
|
|
"peers:remove": "rm -rf node_modules/react node_modules/react-dom",
|
|
|
|
"prebuild": "npm run peers:remove",
|
|
"build": "echo pretend this command built your library",
|
|
|
|
"pretest": "npm run peers:install",
|
|
"test": "echo pretend this command built your library",
|
|
}
|
|
}
|
|
```
|
|
|
|
## Still having trouble? Check your sub-dependencies
|
|
|
|
Even after all of the above work I still ran into trouble. It turns out, this issue flows all the way down to literally any additional copy of `react` or `react-dom` from any dependency in the chain. I discovered this by wiping out my dependencies in large sections until my library successfully compiled for the consuming app, and then proceeded to narrow it down.
|
|
|
|
For me, I discovered that [storybook-mobile](https://github.com/aholachek/storybook-mobile), an optional storybook addon I was using, was having the very same issue of including `react` and `react-dom` in its `devDependencies`. It's an easy issue to miss, so I [wrote up a detailed issue](https://github.com/aholachek/storybook-mobile/issues/25) and [submitted a PR to fix](https://github.com/aholachek/storybook-mobile/pull/26) it. Thankfully, this turned out to be a success story and the latest release works wonderfully.
|
|
|
|
---
|
|
|
|
So in conclusion, be super careful that there aren't more than one `react` or `react-dom` dependencies _anywhere at all in your dependency tree_ besides in your main app. Only one copy may exist anywhere at any time.
|