2

I have a component library that exports components

// Test.tsx
import React from 'react';
const Test = () => <p>Test</p>;
export default Test;

and

// TestB.tsx
import React from 'react';
import { Typography } from '@material-ui/core';

const TestB = () => <Typography>TestB</Typography>;

export default TestB

In my other package I'm importing the components as so

import React from 'react';
import { Box } from '@components';
import { Test, TestB } from 'component-library';

const Example = (): JSX.Element | null => {
  return (
    <Box mb={3}>
      <Test />
      <TestB />
    </Box>
  );
};

export default Example;

When I import Test I get the expected <p>Test</p> but when I import TestB I get the following error:

react.development.js:1476 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
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
    at resolveDispatcher (react.development.js:1476)
    at Object.useContext (react.development.js:1484)
    at useTheme (useTheme.js:4)
    at useStyles (makeStyles.js:222)
    at WithStyles (withStyles.js:55)
    at renderWithHooks (react-dom.development.js:14803)
    at updateForwardRef (react-dom.development.js:16816)
    at beginWork (react-dom.development.js:18645)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
  1. I have confirmed react and react-dom are both on the version 16.9.0 -- though this doesn't seem relevant since it will work with Test, but not TestB

  2. I don't see how I could be breaking the Rules of Hooks with the implementation of TestB since it doesn't use any hooks

  3. I have tried removing all but one version inclusion of react through package.json -- again this doesn't seem relevant since it will work with Test, but not TestB

Component Library package.json dependencies

  "dependencies": {
    "@material-ui/core": "4.11.4",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-is": "^17.0.2",
    "react-svg": "^13.0.6"
  },
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@types/jest": "^24.0.24",
    "@typescript-eslint/eslint-plugin": "^4.25.0",
    "@typescript-eslint/parser": "^4.25.0",
    "babel-loader": "^8.2.2",
    "eslint": "^7.27.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-plugin-import": "^2.23.3",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.23.2",
    "eslint-plugin-react-hooks": "^4.2.0",
    "jest": "^24.9.0",
    "rollup": "^1.27.13",
    "rollup-plugin-commonjs": "^10.1.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-peer-deps-external": "^2.2.0",
    "rollup-plugin-typescript2": "^0.25.3",
    "standard": "^14.3.1",
    "standard-prettier": "^1.0.1",
    "ts-jest": "^24.2.0",
    "typescript": "^3.7.3"
  },

Projects package.json dependencies

  "dependencies": {
    "@babel/core": "^7.1.6",
    "@babel/plugin-proposal-class-properties": "^7.1.0",
    "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-flow-strip-types": "^7.1.6",
    "@babel/plugin-transform-modules-commonjs": "^7.4.3",
    "@babel/polyfill": "^7.6.0",
    "@babel/preset-env": "^7.9.0",
    "@babel/preset-react": "^7.0.0",
    "@babel/preset-typescript": "^7.9.0",
    "@date-io/moment": "1.x",
    "@emotion/core": "^10.0.10",
    "@material-ui/core": "^4.11.4",
    "@material-ui/icons": "^4.9.1",
    "@material-ui/lab": "^4.0.0-alpha.56",
    "@material-ui/pickers": "^3.2.10",
    "@stripe/react-stripe-js": "^1.1.2",
    "@stripe/stripe-js": "^1.4.0",
    "@styled-system/css": "^1.0.3",
    "@typeform/embed": "^0.5.12",
    "attr-accept": "^1.1.0",
    "autoprefixer": "^8.0.0",
    "awesome-typescript-loader": "^4.0.0",
    "axios": "^0.19.2",
    "babel-loader": "^8.0.4",
    "babel-plugin-dynamic-import-node": "^2.3.0",
    "babel-plugin-styled-components": "^1.10.0",
    "classnames": "^2.2.5",
    "compression-webpack-plugin": "^1.0.0",
    "core-js": "^3.2.1",
    "cropperjs": "^1.3.3",
    "css-hot-loader": "^1.3.6",
    "css-loader": "^0.28.7",
    "d3": "^6.2.0",
    "d3-selection": "^2.0.0",
    "date-fns": "^2.9.0",
    "dot-prop-immutable": "^1.4.0",
    "downshift": "^5.0.3",
    "emotion-theming": "^10.0.10",
    "empty": "^0.10.1",
    "enzyme": "^3.6.0",
    "enzyme-adapter-react-16": "^1.5.0",
    "enzyme-to-json": "^3.3.4",
    "eslint": "^7.3.1",
    "eslint-config-airbnb": "^16.1.0",
    "eslint-plugin-flowtype": "^2.46.1",
    "eslint-plugin-import": "^2.8.0",
    "eslint-plugin-jest": "^21.5.0",
    "eslint-plugin-jsx-a11y": "^6.0.2",
    "eslint-plugin-react": "^7.5.1",
    "extract-text-webpack-plugin": "^3.0.1",
    "faker": "^4.1.0",
    "fetch-mock": "^6.5.2",
    "file-loader": "^0.11.2",
    "file-saver": "^2.0.2",
    "final-form": "^4.20.1",
    "final-form-arrays": "^3.0.2",
    "final-form-calculate": "^1.3.1",
    "glob": "^7.1.2",
    "hellosign-embedded": "^2.8.0",
    "identity-obj-proxy": "^3.0.0",
    "isomorphic-fetch": "^2.2.1",
    "js-yaml": "^3.10.0",
    "jwt-decode": "^2.2.0",
    "lint-staged": "^10.2.11",
    "loaders.css": "^0.1.2",
    "lost": "^8.2.0",
    "components": "https://github.com/example/components#dev",
    "mock-geolocation": "^1.0.11",
    "moment": "2.24.0",
    "moment-locales-webpack-plugin": "^1.2.0",
    "moment-timezone": "^0.5.28",
    "node-sass": "^4.12.0",
    "notistack": "^0.9.11",
    "numeral": "^2.0.6",
    "path-complete-extname": "^1.0.0",
    "postcss-automath": "^1.0.1",
    "postcss-calc": "^7.0.2",
    "postcss-flexbugs-fixes": "^3.2.0",
    "postcss-font-magician": "^2.0.0",
    "postcss-import": "^10.0.0",
    "postcss-initial": "^2.0.0",
    "postcss-loader": "^2.0.6",
    "postcss-smart-import": "^0.7.5",
    "precss": "^2.0.0",
    "prettier-eslint": "^8.7.0",
    "prop-types": "^15.5.10",
    "qs": "^6.5.1",
    "ramda": "^0.27.1",
    "rc-time-picker": "^3.7.2",
    "react": "17.0.2",
    "react-addons-shallow-compare": "^15.6.0",
    "react-confetti": "^6.0.0",
    "react-content-loader": "^4.0.1",
    "react-cropper": "^1.0.1",
    "react-dates": "^21.8.0",
    "react-datetime": "^2.16.3",
    "react-dom": "17.0.2",
    "react-final-form": "^6.5.1",
    "react-final-form-arrays": "^3.1.2",
    "react-final-form-listeners": "^1.0.3",
    "react-hot-loader": "^4.6.3",
    "react-measure": "^2.0.2",
    "react-modal": "^3.8.1",
    "react-motion": "^0.5.1",
    "react-onclickoutside": "^6.7.0",
    "react-portal": "^4.0.0",
    "react-pose": "^4.0.8",
    "react-pose-text": "^3.1.0",
    "react-query": "^2.5.13",
    "react-redux": "^7.2.0",
    "react-resize-detector": "^6.6.0",
    "react-router": "^5.1.2",
    "react-router-dom": "^5.1.2",
    "react-select": "^1.0.0-rc.10",
    "react-svg-loader": "^1.1.1",
    "react-switch": "^2.0.0",
    "react-table": "^7.0.1",
    "react-tabs": "^2.2.2",
    "react-test-renderer": "^16.5.2",
    "react-text-mask": "^5.4.3",
    "react-textfit": "^1.1.0",
    "react-use": "^15.3.4",
    "react-virtualized-auto-sizer": "^1.0.2",
    "react-window": "^1.8.6",
    "react-window-infinite-loader": "^1.0.5",
    "react-youtube": "^7.5.0",
    "react_ujs": "^2.4.4",
    "recompose": "^0.26.0",
    "redux": "^4.0.1",
    "redux-actions": "^2.6.5",
    "redux-analytics": "^0.3.1",
    "redux-form": "^8.3.6",
    "redux-mock-store": "^1.4.0",
    "redux-observable": "^1.1.0",
    "redux-persist": "^5.6.12",
    "redux-responsive": "^4.3.8",
    "redux-thunk": "^2.2.0",
    "regenerator-runtime": "^0.11.0",
    "reselect": "^3.0.1",
    "resolve-url-loader": "^2.1.0",
    "rxjs": "^6.4.0",
    "sass-loader": "^6.0.6",
    "shortid": "^2.2.8",
    "sinon": "^6.3.5",
    "start-server-and-test": "^1.7.11",
    "style-loader": "^0.18.2",
    "styled-components": "^4.1.3",
    "styled-normalize": "^8.0.4",
    "styled-system": "^3.2.1",
    "svg-react-loader": "^0.4.5",
    "swr": "^0.2.0",
    "ts-jest": "^23.1.3",
    "typescript": "^3.8.3",
    "typings-for-css-modules-loader": "^1.7.0",
    "warnings-to-errors-webpack-plugin": "^2.0.1",
    "webpack": "^3.12.0",
    "webpack-bundle-analyzer": "^3.0.3",
    "webpack-dev-server": "^2.7.1",
    "webpack-manifest-plugin": "^1.3.1",
    "webpack-merge": "^4.1.0",
    "whatwg-fetch": "^3.0.0"
  },
  "devDependencies": {
    "@svgr/cli": "^5.4.0",
    "@testing-library/cypress": "^6.0.0",
    "@testing-library/react": "^8.0.6",
    "@types/classnames": "^2.2.3",
    "@types/faker": "^4.1.4",
    "@types/fetch-mock": "^6.0.3",
    "@types/intercom-web": "^2.8.7",
    "@types/jest": "^25.2.3",
    "@types/jwt-decode": "^2.2.1",
    "@types/moment-timezone": "^0.5.4",
    "@types/qs": "^6.5.1",
    "@types/query-string": "^6.3.0",
    "@types/ramda": "^0.27.36",
    "@types/react": "^16.7.20",
    "@types/react-content-loader": "^3.1.4",
    "@types/react-dates": "^17.1.14",
    "@types/react-dom": "^16.0.11",
    "@types/react-loadable": "^5.4.2",
    "@types/react-measure": "^2.0.2",
    "@types/react-modal": "^3.8.2",
    "@types/react-motion": "^0.0.26",
    "@types/react-onclickoutside": "^6.0.3",
    "@types/react-redux": "^7.1.9",
    "@types/react-router-dom": "^5.1.3",
    "@types/react-select": "^1.2.7",
    "@types/react-table": "^7.0.13",
    "@types/react-test-renderer": "^16.0.2",
    "@types/recompose": "^0.26.1",
    "@types/redux-actions": "^2.6.0",
    "@types/redux-form": "^7.4.16",
    "@types/redux-mock-store": "^1.0.0",
    "@types/shortid": "^0.0.29",
    "@types/sinon": "^5.0.5",
    "@types/styled-components": "^4.1.18",
    "@types/styled-system": "^3.1.1",
    "@types/webpack-env": "^1.13.6",
    "@typescript-eslint/eslint-plugin": "^3.4.0",
    "@typescript-eslint/parser": "^3.4.0",
    "cypress": "6.1.0",
    "eslint-config-prettier": "^6.11.0",
    "eslint-plugin-prettier": "^3.1.4",
    "jest": "^26.0.1",
    "miragejs": "^0.1.35",
    "pre-commit": "^1.2.2",
    "prettier": "^2.0.5"
  },

Any thoughts?

Seth McClaine
  • 9,142
  • 6
  • 38
  • 64
  • Show us how you're importing and using `TextB` – Dominik Jun 03 '21 at 22:23
  • @Dominik updated – Seth McClaine Jun 03 '21 at 22:41
  • Can you console.log(TestB) before the return statement to ensure it is a component/function? – Jordan The Genius Jun 03 '21 at 23:01
  • @JordanTheGenius {$$typeof: Symbol(react.element), type: {…}, key: null, ref: null, props: {…}, …} $$typeof: Symbol(react.element) key: null props: {children: "-Tested-"} ref: null type: {$$typeof: Symbol(react.forward_ref), propTypes: {…}, Naked: {…}, options: {…}, render: ƒ, …} _owner: null _store: {validated: false} _self: null _source: null __proto__: Object – Seth McClaine Jun 03 '21 at 23:04
  • the versions are relevant, Typography uses hooks inside, that's why you're getting the error in TestB, try setting the same react version in the project as in your component package – diedu Jun 04 '21 at 04:35
  • @diedu, I had already tried matching versions previously, it doesn't make a difference. I went ahead and tried again bumping up to 17.0.2 in the package, also removing all components that have dependencies on react and react-dom (primarily storybook) from the Project with no luck. (I will update question with new package.json files) – Seth McClaine Jun 04 '21 at 14:07
  • I can also remove react and react-dom from the project and let it use the copy that is brought in by component-library and still run into the issue – Seth McClaine Jun 04 '21 at 14:28
  • could you check with `npm list | grep react-dom` if you have a different version installed from other packages in your project – diedu Jun 04 '21 at 15:05
  • @diedu I noticed some of the packages weren't up for react & react-dom 17.* so bumped them back down to 16.14.0 for both Heres the output: npm list | grep react-dom\@ ├─┬ @types/react-dom@16.9.13 npm ERR! peer dep missing: cypress@^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0, required by @testing-library/cypress@6.0.1 npm ERR! peer dep missing: eslint@^4.9.0, required by eslint-config-airbnb@16.1.0 ... (a lot more extraneous errors) ... │ ├── react-dom@16.14.0 extraneous ├─┬ react-dom@16.14.0 – Seth McClaine Jun 04 '21 at 15:50

2 Answers2

1

Moving everything to devDependencies for the most part and adding the prepare script seemed to be what I was missing.

My package.json ended up looking like this

{
  "version": "0.1.0",
  "main": "dist/index.js",
  "module": "build/index.es.js",
  "jsnext:main": "build/index.es.js",
  "files": ["dist", "build"],
  "scripts": {
    "prepare": "npm run build",
    "build": "rollup -c",
    "format": "prettier-standard --format",
    "test": "jest --coverage",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook",
    "lint": "eslint 'src/**/*.{{j,t}s,{j,t}sx}'",
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {},
  "peerDependencies": {
    "@material-ui/core": "4.11.4",
    "react": "^16"
  },
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@material-ui/core": "4.11.4",
    "@storybook/addon-actions": "^6.2.9",
    "@storybook/addon-essentials": "^6.2.9",
    "@storybook/addon-knobs": "^6.2.9",
    "@storybook/addon-links": "^6.2.9",
    "@storybook/react": "^6.2.9",
    "@types/jest": "^24.0.24",
    "@typescript-eslint/eslint-plugin": "^4.25.0",
    "@typescript-eslint/parser": "^4.25.0",
    "babel-loader": "^8.2.2",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-plugin-import": "^2.23.3",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-plugin-react": "^7.23.2",
    "eslint": "^7.27.0",
    "jest": "^24.9.0",
    "react-dom": "^17.0.2",
    "react-is": "^17.0.2",
    "react-svg": "^13.0.6",
    "react": "^17.0.2",
    "rollup-plugin-commonjs": "^10.1.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-peer-deps-external": "^2.2.0",
    "rollup-plugin-typescript2": "^0.25.3",
    "rollup": "^1.27.13",
    "standard-prettier": "^1.0.1",
    "standard": "^14.3.1",
    "storybook-addon-paddings": "^4.1.1",
    "storybook-dark-mode": "^1.0.8",
    "ts-jest": "^24.2.0",
    "typescript": "^3.7.3"
  },
  "jest": {
    "preset": "ts-jest",
    "testEnvironment": "node"
  },
  "standard": {
    "ignore": [
      "node_modules/",
      "build/"
    ]
  }
}

And rollup.config by request

import typescript from 'rollup-plugin-typescript2'
import commonjs from 'rollup-plugin-commonjs'
import external from 'rollup-plugin-peer-deps-external'
import resolve from 'rollup-plugin-node-resolve'

import pkg from './package.json'

export default {
  input: 'src/index.ts',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      exports: 'named',
      sourcemap: true
    },
    {
      file: pkg.module,
      format: 'es',
      exports: 'named',
      sourcemap: true
    }
  ],
  external: ["react", "@material-ui/core", "react-is"],
  plugins: [
    external(),
    resolve(),
    typescript({
      rollupCommonJSResolveHack: true,
      exclude: [
        '**/__tests__/**',
        '**/*.stories.*'
      ],
      clean: true
    }),
    commonjs({
      include: ['node_modules/**'],
      namedExports: {}
    })
  ]
}
Seth McClaine
  • 9,142
  • 6
  • 38
  • 64
  • Any reason you needed material-ui and react as peerDependencies? was that trial by error? – svict4 Aug 02 '21 at 12:45
  • Could you share your rollup config? did you do anything with `@rollup/plugin-node-resolve` or `rollup-plugin-peer-deps-external`? – svict4 Aug 02 '21 at 13:12
  • My understanding is peer dependencies calls out it can expect the host package to already have that package. Heres an article https://nodejs.org/es/blog/npm/peer-dependencies/ – Seth McClaine Aug 02 '21 at 16:07
0

I had exactly the same setup and problem, but I focussed too much on the problem, that the imported MUI-component is calling this error. But when you read the error message closely, there is also the possibility of "You might have more than one copy of React in the same app". The reason for my wrong conclusion was that my app did not throw an error once I removed the MUI component. Accordingly, it could not be the React error. Unfortunately, this was not the case! After some more desperate research on every detail, I found this post: Invalid Hook call....

So inside the APP folder (which is using your library) I ran the command npm link <path_to_local_library>/node_modules/react, which fixed my error. See the details for npm link.

JaneMcBrain
  • 330
  • 3
  • 10