React conflict when consuming a local react library - reactjs

I have a Parent React App that consumes a module library which is also a React app. I used to have react and react-dom installed in the parent project and have them added as a peerDependencies in package.json in the modules library. But, now I need to install the two packages as devDependencies in the modules library.
This resulted in Invalid Hook Error, which I narrowed down to the third point:
you might have more than one copy of React in the same app
Parent App's package.json:
{
// Other Deps..
"modules-lib": "file:../libs/modules-lib/dist",
}
I've tried a couple of solutions from this issue, but things didn't work. I also tried linking react and react-dom using npm link from our modules library to the parent app and it actually worked.
I was wondering that why does the consumer of the modules library looks into its node_modules folder and why the two packages are being listed as dependencies and not devDependencies in the final build folder in package.json

Related

How to use Apollo/Graphql hooks within external React.js component library

I have 3 React.js projects and I'm trying to consolidate the reused components across all 3 projects into one shared component library.
This component library is hosted on Github and imported into all 3 projects through the package.json file like this:
"components": "github:myusername/components"`
I am then able to import the components from the library into each project like such:
import { Form } from "components"
but when the component being imported (in this case a Form) uses an Apollo/Graphql hook (useQuery, or useMutation) I get this error:
Uncaught Invariant Violation: Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance in via options.
I've found countless questions about this same error, but the solutions are either already in place or irrelevant. Here is what I've found so far:
Wrap the App with the <ApolloProvider client={client}> tag (this is already there in the 3 projects - but not in the "components" library)
use the #apollo/client package instead of the #apollo/react-hooks - link (already using that package)
The solution that worked for me was to define #apollo/client as a peer dependency in the root package.json of my shared lirbary (I'm using lerna).
It didn't work for me to just link the package with yarn link but I had to publish it on npm.
I looked into this a little more given Kn3cht's information that the shared component works if published to NPM and found a beautiful solution. There is a utility called yalc that works perfectly. The problem with npm/yarn link is that symlinked dependencies are not deduped. To use, install yalc globally (eg, npm install yalc --global or yarn global add yalc).
Let's say the component to be shared is called shared-component.
In the root folder of shared_component, type 'yalc push'.
In the root folder of component that will import the shared_component, type 'yalc link shared-component'
That's it. I can't believe this utility is not more widely known. I believe you still need to designate react and apollo dependencies as peerDependencies in package.json.
As for Lerna, there is a small wiring needed to have updates automatically pushed whenever the shared-component changes. Essentially, install watch at the root of the mono repo as a dev dependency, then add scripts to the root and all component package.json files that you want to have automatically pushed. For example, the target I create is called "watch:push". In the root package.json:
"scripts": {
"watch:push": "lerna run watch:push"
}
For each component that I want to share I add this target to its package.json:
"scripts": {
"push": "yalc push",
"watch:push": "watch 'yarn push' ./dist",
}
Here you can manually push your shared-component via yarn push, or you can have the shared-component pushed whenever the contents of the build folder have changed (in my case the folder is dist).
So to make this fully automatic, you need to run run a command separately that executes a build whenever source code changes.

Trying to create a shareable React component - but failing to import it

I'm trying to share a React component I've created through a local hosted npm repo.
To do that I created the React component with typescript, transpiled it to js and published the resulting code to the repo. But when I install this package in an existing project (a basic create-react-app project with typescript) and try to use that component - My app tried to compile it for a few minutes and I fail to load that component. Sometimes if I wait a few minutes I see this error - although the component was tested and works:
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 See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem.'
I've copied the same component to be embedded in the app and not installed by npm - it worked. I tried to strip the component to the bare minimum - it still takes a long time.
The steps to reproduce are easy:
I've shared the component in github:
https://github.com/ymoran00/example-stackoverflow-react
To build it you need to run npm install and then npm run build.
the result will be generated in the lib folder.
You can then go into the lib folder and run:
npm link
Then create a new typescript create-react-app project:
npx create-react-app my-app --template typescript
Go inside it and run:
npm link login-component
This will install the linked package.
Now go to App.tsx and import the package:
import LoginContainer from 'login-component/LoginContainer';
And use it in the App:
<LoginContainer onLogin={()=> {alert('success')}}/>
Run the app with npm start.
The App will open the browser - but nothing will load. It's kind of stuck on build or whatever - I don't know what happens there. If you'll take a look at the component you'll see it's quite a basic one with Material-UI.
The first place that I looked was your package.json file because it most likely that you are dealing with reason #1:
You might have mismatching versions of React and the renderer (such as React DOM)
I see that you are including react and react-dom as dependencies for your component. You should move these from dependencies to peerDepenedencies. You likely want to move #material-ui/core and #material-ui/icons to peerDependencies as well. Right now React is being bundled with your component and your component uses its own React version rather than the one in your app. You want to make it so that projects that use your component are expected to include React on their own.
For more information about why you should use peerDependencies, see this question:
What's the difference between dependencies, devDependencies and peerDependencies in npm package.json file?
It seems that the main problem I had in the process is using npm link.
It causes problems when combined with hooks - that's why I get this hooks error.
See also in this thread:
https://github.com/facebook/react/issues/13991
So instead of using npm link I've started using npm-sync and it seems to solve the problem.

Using StorybookJS in a component library package with React as peer dependency

I am creating my own React component library. The package.json is using react and react-dom as peer dependencies because I only want to ship code for components only. This package will then be used in other React-based projects so I will provide React myself in those projects.
I've decided for adding StorybookJS in the package for two reasons
Help during development to preview my components
Create a static storybook site that I will host somewhere showcasing the components
My project structure:
package.json
src/
components/ // contains components
storybook/ // <-- not part of the package
index.ts // <-- this is entry point of the package
However this introduces a problem. Since StorybookJS requires react and react-dom to run, I would have to include them as dependencies of the library.
I will be using webpack to actually bundle only the component code so it can be distributed. Should I just use externals property in configuration to exclude react and react-dom from the bundle? I guess it would work but then the package.json would still list React as its dependencies (which I do not want).
What is the correct approach here?
I ended up putting react and react-dom into peerDependencies as that is correct approach in my opinion. Since that package file belongs to library itself (and not Storybook), it should correctly state its dependencies.
To avoid bundling React into my library bundle, I put it into externals webpack configuration like so:
externals: {
react: 'react',
'react-dom': 'react-dom',
},
This way, React or ReactDOM does not end up in the bundle. The library is React component library so it can't be used without React anyway and you should provide it.
The final step to make Storybook work, I added prepare script that installs peer dependencies when you install the package. That way React and ReactDOM are provided for Storybook application.
This is the prepare script in my package.json:
"scripts": {
"prepare": "install-peers && npm run build"
}
I used install-peers package to install React and ReactDOM. For newer version of NPM client, you will not need this package and running npm install should install peer dependencies as well.

How to use Storybook components (and Lerna) in an external project done with Next.js?

I've just created my Storybook library of components (ES6 etc).
It is structured as a Lerna project (all components isolated in the packages/ folder). However, that's a private repo with no real publish feature so, I think Lerna won't work with a private (free) account.
I've pushed the storybook repo to my Bitbucket as it is.
Now, I'd like to use my storybook library of components from the main application which is a different repo (on Bitbucket) build on Next.js.
I was attempting to import the individual storybook components as follows
import MyComponent from 'storybook-repo/packages/my-component/my-component';
but it obviously doesn't work, returning this error:
Module parse failed: Unexpected token (8:9)
You may need an appropriate loader to handle this file type.
This, because MyComponent is a jsx file.
I was hoping Next.js to transpile the imported modules but this is not the case.
My questions are:
My guts say the import of the whole storybook as git+ssh://git#bitbucket.org/myusername/storybook-repo.git from package.json is not a good idea. Any better solutions?
Is it true that Lerna works only for public/Pro repos where I can publish my packages?
Why is Next.js not transpiling the imported jsx modules? At this point, how does this process work? Shall I transpile the storybook components from the remote repo or do the job from my main application?
Thanks
On my last project, we used Rollup.js to create a dist build of just the components we had developed in Storybook. Our components were located in src/components directory. We maintained an index.js for the components using an internal scaffolding tool. We published our dist folder as a privately scoped NPM package and then pulled in our components from there.
For local development, we used Webpack aliasing to check the current environment and either pull the published NPM package or pull directly from the storybook/dist folder that we were building to.
There's a great guide on building this here. Hope this points you in the right direction. As an alternative, I believe you can fiddle with next.config.js to override the Webpack config and make sure that your external imports get sucked up through the same Webpack, but, you also have to add a few rules to the .babelrc on the Storybook side to ensure that it gets ignored over there. We found it to be easier to just publish a package and bundle everything up.

Is Shrinkwrap preventing me from updating react and react-dom?

I am trying to update react from 15.4.2 to 16.2.0. Main dependencies I am concerned about is React and React-Dom. I am trying to run: npm uninstall --save react react-dom and then npm install --save react react-dom however I keep getting the message: UNMET PEER DEPENDENCY react#16.2.0 react-dom#16.2.0 so this makes me think it's a shrink or tied down dependency but can't seem to find it. I have tried to even remove react and react-dom from the dependencies in package.json. Still no avail.
Issue:
My root issue is I coudn't run material ui components with tap react and more specifically the Tabs and it's touch/tap feature. Real problem is I believe I had other node_modules,package.jsons in root folder where my app/components were in so more than one node_module and package.json files and folders which caused the clash.
To resolve:
Make a copy of your business logic from app where you cannot update
Then navigate into/create new directory and
Scaffolded new react project in there (react-app or in my case Yo #microsoft/sharepoint)
Checked the react, reactdom versions in node_modules folder and package.json from within the application that's not updating to see if they're latest versions...they weren't so I did the following updates:
3.2. npm install --save react react-dom (you may have to run npm uninstall react react-dom first)
3.3. npm install material-ui#latest
3.4 npm i --save react-tap-event-plugin#3.0.2
Check if versions have updated in folders above in step 3...they have now.
Try to import and create tap react app components again e.g. Tabs and now it should work
Conclusion:
It was not Shrinkwrap itself that was causing the issue but the duplication of node_module files and/or package.json files. See Material-UI Tabs have stopped working for more info.
Also remember Create React app will not work with some versions of React and React Dom. So be wary of this.
Some things you could try:
check the version of react and react-dom inside the node_modules/ folder
remove node_modules/ and reinstall it again to remove any stall dependencies

Resources