17

Having a lot of trouble trying to set up a common UI library.

I've set up a yarn workspace which looks like this:

/monorepo
   /common-16.13
   /react-app-16.8.
   /another-app-16.13

I then import common-16.13 into react-app-16.8 and use one of the components like this:

 /react-app/home.js
    import {SharedComponent} from "common"

However when I run the application I get this error:

react.development.js?80c6:1465 Uncaught 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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

Inside common I have:

/src/components/SharedComponent.jsx:

    import React from 'react';
    import { Box } from 'material-ui/core';
    export const ShareComponent = ()=> <Box>SharedComponent</Box>;

/src/components/index.js:

   export { SharedComponen t} from 'SharedComponent';

/src/index.js:

   export {SharedComponent } from './components';

package.json:

  {  
       "name": "@libs/common",
       "main": "dist/index.js",
       "scripts" {
          "build": "webpack"
       }
  }

/common/webpack.config.json: 

    const webpack = require('webpack');

    module.exports = env => {
        // Each key value generate a page specific bundle
        entry: {
          index: './src/index.js'
        },

        output: {
          path: path.resolve(ROOT_PATH, 'dist'),
          publicPath: '/',
          filename: 'index.js',
          library: '@libs/common',
          libraryTarget: 'umd'
        },

        module: {
          rules: [
            {
              test: /\.(js|jsx)$/,
              use: 'happypack/loader?id=jsx',
              exclude: /node_modules/
            }
          ]
        },
        // Automatically resolve below extensions
        // Enable users to leave off the extensions when importing
        resolve: {
          symlinks: false,
          extensions: ['*', '.js', '.jsx', '.css', '.scss']
        },

        plugins: [
          new HappyPack({
            id: 'css',
            threadPool: happyThreadPool,
            loaders: [
              'cache-loader',
              'style-loader',
              {
                loader: MiniCssExtractPlugin.loader,
                options: {
                  hmr: true
                }
              },
              'css-loader',
              'sass-loader'
            ]
          }),
          new HappyPack({
            id: 'jsx',
            threadPool: happyThreadPool,
            loaders: [
              'cache-loader',
              {
                loader: 'babel-loader'
              }
            ]
          })
        ]
      }

So I bundle common. Then in my react-app I yarn install @lib/common. Then I import SharedComponent into my react app:

/react-app/src/index.js:

   import { SharedComponent } from '@libs/common';

/react-app/webpack.config.js:

{
  // Each key value generate a page specific bundle
  entry: {
    index: './src/index.jsx',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },

    ]
  },
  // Automatically resolve below extensions
  // Enable users to leave off the extensions when importing
  resolve: {
    extensions: ['*', '.js', '.jsx', '.css', 'scss'],
    alias: {
      react: path.resolve('./node_modules/react'),
    }
  },
  output: {
    path: path.resolve(ROOT_PATH, 'dist'),
    publicPath: '/',
    filename: '[name].bundle.js',
    chunkFilename: '[id].bundle.js'
  },
};

It bundles fine but when I run the application I run into the error above. I can't tell if it's related to how i'm exporting my common components, but it it seems right. I read I should have a react alias in my app, which I do. I'm using yarn workspaces and not sure if that's related somehow.

Batman
  • 5,563
  • 18
  • 79
  • 155
  • 1
    Hooks are not compatible with Class components. To use Hooks, you convert the class components to function components. – DevLoverUmar Apr 17 '20 at 03:29
  • Is this true for the entire tree? For example my common library has a function component that includes a hook. Does that mean I cannot import that component into anything that leads to a class component at the top level? – Batman Apr 17 '20 at 03:31
  • No, you can do that. Can you please share the code chunk having error? – DevLoverUmar Apr 17 '20 at 03:32
  • It's just an opinion but for code compilation you can use react-scripts. I recently had a same situation of trying with webpack/gulp and finally compiled my code successfully with react-scripts. – DevLoverUmar Apr 17 '20 at 03:36
  • 2
    I can't move away from webpack at the moment in my project. I think it's a fixable issue however, I'm sure other people have had to deal with similar setups – Batman Apr 17 '20 at 03:49
  • THe issue is that you are running more than 1 instance of React when you are setting up your monorepo. You need to move React as part of peer dependencies. Try adding this `"peerDependencies": { "react": "^16.11.0", "react-dom": "^16.11.0" },` on your common package's package.json? – wentjun Apr 17 '20 at 04:38
  • @wentjun why 16.11? – Batman Apr 17 '20 at 07:49
  • @Batman its just an example. My point is that, the peer depencency react version should be the same as the main component you are importing into – wentjun Apr 17 '20 at 08:08
  • So am I suppose to use 16.8 in my peerDependencies for common? How come I can import something like Material UI which I assume has a different React version then what my app is on? – Batman Apr 17 '20 at 08:14
  • @batman because material-ui has react as a peer dependency, not a dependency. It uses the version of react that you install for it. – Jared Smith Apr 17 '20 at 19:10
  • 1
    So I've made that peerDependency change but babel-loader is failing to import the package. – Batman Apr 17 '20 at 19:28
  • @Batman Still no solution to this problem! We've run into a very similar issue, does anybody know a reliable way to use a shared React UI library with workspaces? – Avi C Apr 28 '21 at 02:00
  • I think I had to make sure all projects were under the same react version, it's kind of annoying and I don't use the workspace setup often because of this. – Batman Apr 28 '21 at 08:23
  • I made a repo to show this issue. I'm running into it too and haven't been able to find a fix. I definitely only have one version of React. https://github.com/kauffmanes/invalid-hooks-repro – intrepid_em Aug 30 '21 at 20:20

3 Answers3

3

Run the following command:

yarn why react

If the result shows that you have multiple versions of react:

  1. Remove all local installations
  2. Install a single version of React in the root workspace instead
Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
  • 1
    do i understand correct that monorepo does not allow different versions of react for each project? – Asking Aug 03 '22 at 16:54
2

this is probably a bug coming from yarn

issue: https://github.com/yarnpkg/yarn/issues/8540

I did a workaround by:

  1. exporting my common package into a new private github repo

  2. create access token https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token

  3. in my package.json dependencies I added:

  "common": "git+https://{accessToken}:x-oauth-basic@github.com/{user}/{repo}.git",
yonadav bar ilan
  • 550
  • 6
  • 10
2

It happened to me when when migrating existing project to mono repo. It was caused because I copied the lock files into the packages folders. I've solved it by deleting any node_modules and any lock(yarn.lock and package-lock) from any package folder and then running yarn install on root directory.

Eliav Louski
  • 3,593
  • 2
  • 28
  • 52