React: Hooks error on React library compiled with Rollup.js

Created on 29 Jan 2019  Â·  24Comments  Â·  Source: facebook/react

Disclaimer: I'm not sure if this is an specific bug from React's Hooks or it is something I'm missing in my Rollup.js configuration but I've been trying a lot of different things and always end up in the same error.

Hooks can only be called inside the body of a function component.

I'm building a React Component library using the alpha version of React and Hooks, and using Rollup.js v1.1.2 as a bundler with the next configuration:

import { readdirSync } from 'fs';
import path from 'path';
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import replace from 'rollup-plugin-replace';
import resolve from 'rollup-plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';

const CODES = [
  'THIS_IS_UNDEFINED',
  'MISSING_GLOBAL_NAME',
  'CIRCULAR_DEPENDENCY',
];

const getChunks = URI =>
  readdirSync(path.resolve(URI))
    .filter(x => x.includes('.js'))
    .reduce((a, c) => ({ ...a, [c.replace('.js', '')]: `src/${c}` }), {});

const discardWarning = warning => {
  if (CODES.includes(warning.code)) {
    return;
  }

  console.error(warning);
};

const env = process.env.NODE_ENV;

const plugins = [
  external(),
  babel({
    exclude: 'node_modules/**',
  }),
  resolve(),
  replace({ 'process.env.NODE_ENV': JSON.stringify(env) }),
  commonjs(),
  env === 'production' && terser(),
];

export default [
  {
    onwarn: discardWarning,
    input: 'src/index.js',
    output: {
      esModule: false,
      file: 'umd/library-name.js',
      format: 'umd',
      name: 'libraryName',
    },
    plugins,
  },
  {
    onwarn: discardWarning,
    input: getChunks('src'),
    output: [
      { dir: 'esm', format: 'esm', sourcemap: true },
      { dir: 'cjs', format: 'cjs', sourcemap: true },
    ],
    plugins,
  },
];

For the examples page I created an internal project using create-react-app and I'm importing the library in the package.json using link:..

The page breaks as soon as it gets to the first Hooks call var svgRef = useRef(); with the error mentioned above, but this only happens when I'm using the bundled code from the ESM file not when I'm using the library code directly. That's why I'm not sure if it is a Rollup misconfiguration or a React Hooks bug.

I appreciate any help or guidance on this.

React v16.8.0-alpha.1
React DOM v16.8.0-alpha.1
OS: macOS
Browser: Chrome
Node.js v11.8.0

I did test it with a previous alpha version of React and it happens the same.

Most helpful comment

I can’t guess what’s wrong in your particular setup but I’ve described the issue: you’ll see it when you have two Reacts in the resulting bundle. Check the bundle contents. I can’t help you with setting up Rollup, what its errors mean, or how Node resolutions works — just the React part which is definitely related to that.

Npm link is known for causing this because webpack would “see” React both in your lib folder and in your app folder, and would include both of them in the bundle. A common workaround is to npm link back from your lib’s node_modules/react into your app’s React copy.

All 24 comments

I think this is the same issue I ran into in https://github.com/styled-components/styled-components/pull/2349

You probably have two Reacts.

Specifically I think you’re mistakenly bundling React itself into your library. It should be an external instead.

I don't think this is the case @gaearon so I would like to provide a little bit more context to see if might be something else.

I have React, React DOM, and Styled Components as peer dependencies of my library:

  "dependencies": {
    "color": "^3.1.0",
    "d3-array": "^2.0.3",
    "d3-axis": "^1.0.12",
    "d3-format": "^1.3.2",
    "d3-scale": "^2.1.2",
    "d3-selection": "^1.4.0",
    "d3-shape": "^1.3.3",
    "d3-time-format": "^2.1.3",
    "d3-transition": "^1.2.0",
    "date-fns": "^2.0.0-alpha.27",
    "ramda": "^0.26.1"
  },
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "cross-env": "^5.2.0",
    "flow-bin": "^0.91.0",
    "jest-styled-components": "^6.3.1",
    "react-scripts": "^2.1.3",
    "react-test-renderer": "^16.8.0-alpha.1",
    "rollup": "^1.1.2",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-commonjs": "^9.2.0",
    "rollup-plugin-node-resolve": "^4.0.0",
    "rollup-plugin-peer-deps-external": "^2.2.0",
    "rollup-plugin-replace": "^2.1.0",
    "rollup-plugin-terser": "^4.0.3"
  },
  "peerDependencies": {
    "react": "^16.8.0-alpha.1",
    "react-dom": "^16.8.0-alpha.1",
    "styled-components": "^4.1.3"
  }

Besides having React as a peerDependency I have to install it as a devDependency otherwise I'm getting this error (but I think this is a different issue):

Failed to compile.

../esm/index.js
Module not found: Can't resolve 'react' in '/Users/user/library/esm'

The internal examples page has the following package.json file:

{
  "name": "examples",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "normalize.css": "^8.0.1",
    "react": "^16.8.0-alpha.1",
    "react-dom": "^16.8.0-alpha.1",
    "react-scripts": "2.1.3",
    "library-name": "link:..",
    "styled-components": "^4.1.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

And if I check the node_modules directory only one version of React is installed.

Please let me know if I can provide mode information on this? I would code a pen or something trying to replicate what is happening but I'm not sure if I can do a pen to replicate the Rollup part.

I'm gonna try to publish the code soon in case it might be of some help.

Update

I just try to use the library using npm link from an external create-react-app project using the same configuration and I got the same result. I still need to add React as a devDependency and now I'm getting a different error if I use the production build (a Webpack related error) I guess from create-react-app.

I can’t guess what’s wrong in your particular setup but I’ve described the issue: you’ll see it when you have two Reacts in the resulting bundle. Check the bundle contents. I can’t help you with setting up Rollup, what its errors mean, or how Node resolutions works — just the React part which is definitely related to that.

Npm link is known for causing this because webpack would “see” React both in your lib folder and in your app folder, and would include both of them in the bundle. A common workaround is to npm link back from your lib’s node_modules/react into your app’s React copy.

@davegomez do you have any update on the issue? Did you solved it?

(Currently having the same issue, plus I'm also using typescript)
Thanks!

Update
I solved it following this comment

Hello @eduardosanzbBCG I did solve it. Check out the configuration file I have for Rollup. Is working great!

@davegomez Thank you!

@davegomez I see your configuration file and I'm glad it's working, but can you point me to what the change was specifically that got this working for you?

@lukeivie the problem was exactly what @gaearon mentioned in the comment above.

https://github.com/facebook/react/issues/14721#issuecomment-458757426

If I exclude react and react-dom as devDependencies in my library source, I do not get the hooks error when attempting to use my component in a web app. Unfortunately not having react as a devDependency means I get issues like:

Error while loading rule 'react/no-direct-mutation-state': Cannot find module 'react'

when attempting to use eslint-config-react-app. Naturally, installing react as a devDependency makes the linter work with zero issues but then I am back to the problem of hooks not working. I have set up react and react-dom as external and global in my rollup config but that doesn't fix anything. 2 days of my life gone trying to figure this out :(

@gaearon .. I think this issue is not just happening because of two versions of react, When we use hooks in a component library and bundle it as ES6 distribution, hooks are pushed inside conditions (like if) and we are getting this error. I am including library as file:../ Can you please tell how to overcome this.

I'm facing the same

Hooks can only be called inside the body of a function component.

problem.

Is this problem closed and solved for real? Are we doing something wrong with rollup?

@fedriz maybe this thread helps: https://github.com/facebook/react/issues/13991

I'm facing the same problem. who can help me or tell me how to solve it

@ghostjzf if your problem is because of linking the package locally (and not installing from npm repository), then https://github.com/facebook/react/issues/13991#issuecomment-435587809 solves the problem.

If not that, then the problem is that you should add react as an external dependency in your rollup config, e.g:

  external: [
    'react',
    'react-dom',
  ],

@ghostjzf if your problem is because of linking the package locally (and not installing from npm repository), then #13991 (comment) solves the problem.

If not that, then the problem is that you should add react as an external dependency in your rollup config, e.g:

  external: [
    'react',
    'react-dom',
  ],

thank you for your help, but when I use the “npm link” to test it, it’s also facing the same problem

Not sure if that will help anyone, but what worked for me was:

  • in rollup.config.js setting up externals, just like @adraeth mentioned before, so:
  external: [
    'react',
    'react-dom',
  ],
  • remove react and react-dom from the dependencies and move them to peerDependencies
  • and (I can't stress this enough) remove and reinstall node_modules

Hope that helps 🤞

I was struggling with this problem for quite sometime later realized that this happens only when you use this locally with npm link
and for that https://github.com/facebook/react/issues/13991#issuecomment-435587809 this works fine.
but make sure you apply this to the example project.

I prefer using storybookJS to test the components:)

I can get this to work by deleting the react and react-dom node modules within my shared components library, but i can't figure out how to bundle my rollup so that it doesnt include these

@michaelbayday im having the exact same error, how did u solved this? thanks!

If you're adding the library locally via npm link you just need to add an alias to the resolve key of webpack config

but add the alias in your final app or in your library? @michaelbayday

Final app

Was this page helpful?
0 / 5 - 0 ratings