Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hooks error from external component #15050

Closed
mrbinr opened this issue Mar 7, 2019 · 9 comments
Closed

Hooks error from external component #15050

mrbinr opened this issue Mar 7, 2019 · 9 comments

Comments

@mrbinr
Copy link

mrbinr commented Mar 7, 2019

Do you want to request a feature or report a bug?
bug

What is the current behavior?
We are trying to create an external module which will be imported in main app.
Here is the module :

export const MyHelloComponent = (props) => {
  const [test, setTest] = useState();
  return (<div>Hello</div>);
}

Importing this simple code into main app gives us the error :

Uncaught Invariant Violation: Hooks can only be called inside the body of a function component

Removing hooks this code works fine.

We have followed instructions from https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react but error still thrown

If we link react as mentionned in documentation, works but it only can be done in development not in production.

We are not the only one having this issue Side package with React Hooks failing with Invariant Violation when called in main package

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:

Code from external component :
package.json

{
  "name": "app-component",
  "version": "1.0.0",
  "main": "build/index.js",
  "license": "MIT",
  "private": true,
  "peerDependencies": {
    "react": "^16.8.4",
    "react-dom": "^16.8.4"
  },
  "devDependencies": {
    "@babel/core": "^7.3.4",
    "@babel/plugin-proposal-class-properties": "^7.3.4",
    "@babel/plugin-proposal-object-rest-spread": "^7.3.4",
    "@babel/plugin-transform-react-jsx": "^7.3.0",
    "@babel/plugin-transform-regenerator": "^7.3.4",
    "@babel/preset-env": "^7.3.4",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "css-loader": "^2.1.0",
    "react": "^16.8.4",
    "react-dom": "^16.8.4",
    "style-loader": "^0.23.1",
    "styled-jsx": "^3.2.1",
    "webpack": "^4.29.5",
    "webpack-cli": "^3.2.3"
  },
  "scripts": {
    "build": "./node_modules/.bin/webpack --mode production",
    "dev": "./node_modules/.bin/webpack --mode development --watch"
  },
  "files": [
    "build"
  ],
  "dependencies": {
    "@material-ui/core": "^3.9.2"
  }
}

webpack.config.js

const path = require('path');
const pkg = require('./package.json');

const libraryName= pkg.name;

module.exports = (env, argv) => ({
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'index.js',
    library: libraryName,
    libraryTarget: 'commonjs2',
    publicPath: '/build/',
  },
  devtool: argv.mode !== 'production' ? 'inline-source-map': false,
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        exclude: /(node_modules|bower_components)/,
        use: ['babel-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
    ]
  },

  resolve: {
    alias: {
      'react': path.resolve('./node_modules/react'),
      'react-dom': path.resolve('./node_modules/react-dom'),
    }
  },
  externals: {
    react: "react",
    "react-dom": "react-dom"
  }
});

src/index.js

export * from './components/hello';

src/components/hello.js

import React, { useState } from 'react';
export const MyHelloComponent = (props) => {
  const [test, setTest] = useState();
  return (<div>Hello</div>);
}

Code from main app:
webpack.config.js

module.exports = {
  entry: './src/index.js',
  output: {
    path: __dirname + '/dist',
    publicPath: '/dist/',
    filename: 'bundle.js'
  },
  devServer: {
    contentBase: './public',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      }
    ]
  },
};

package.json

{
  "name": "react-app-shell",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "start": "webpack-dev-server --mode development"
  },
  "dependencies": {
    "react": "^16.8.4",
    "react-dom": "^16.8.4"
  },
  "devDependencies": {
    "@babel/core": "^7.3.4",
    "@babel/preset-env": "^7.3.4",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.2.1"
  }
}

src/index.js

import React from "react";
import ReactDOM from "react-dom";
import {MyHelloComponent} from 'app-component';

class Welcome extends React.Component {
  render() {
    return <><h1>Main App</h1><MyHelloComponent/></>;
  }
}
ReactDOM.render(<Welcome />, document.getElementById("root"));

To be able to make it works, you have to do yarn link after (or before) having built it from component and yarn link "app-component" from main app.

What is the expected behavior?
use hooks from external components works obviously.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.8.4 and with previous version it doest not work too

@gaearon
Copy link
Collaborator

gaearon commented Mar 7, 2019

Please provide a reproducing example as a GitHub project we can clone and try.

@gaearon
Copy link
Collaborator

gaearon commented Mar 7, 2019

To be able to make it works, you have to do yarn link after (or before) having built it from component and yarn link "app-component" from main app.

Isn't this exactly what the link (https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react) suggests?

This problem can also come up when you use npm link or an equivalent. In that case, your bundler might “see” two Reacts — one in application folder and one in your library folder. Assuming myapp and mylib are sibling folders, one possible fix is to run npm link ../myapp/node_modules/react from mylib. This should make the library use the application’s React copy.

This should work (and you say it does).

If you're unhappy with this workaround, please investigate this with your bundler (webpack) or your package manager (e.g. Yarn supports Workspaces which solves this by hoisting). I'm not sure I see what is actionable for React in this issue.

@gaearon gaearon closed this as completed Mar 7, 2019
@gaearon
Copy link
Collaborator

gaearon commented Mar 7, 2019

To clarify again, you will only see this issue if you bundle two copies of React in your app — which is bad by itself. It's not a problem with Hooks, but a problem with your bundling process. Bundling duplicate React is bad for your users too because it increases the payload.

@mrbinr
Copy link
Author

mrbinr commented Mar 8, 2019

Thanks for answers,
In fact we are starting using React in more complex developments than examples in documentation, and may be we are wrong in some use of it.
So ok may be it's not an issue, but it's really strange to have differents behaviour of code when using classic code with class+stats and modern code with function+hooks

There is something incomprehensible for us ...

I've created 2 repo to let you see our code :
App Main
App Child

We're also looking about yarn workspaces and webpack to see if there is a solution using them...

If you have some advices on how to do with this kind of development, thanks in advance

@gaearon
Copy link
Collaborator

gaearon commented Mar 8, 2019

Check out these resources: https://reactjs.org/community/support.html

We use issue tracker for bugs in React. But this is not a bug, it’s intended behavior. If you have duplicate React in the bundle you’ll have issues. You need to figure out why you have a duplicate React, and fix the root of the problem. Since that depends more on how you bundle the app than on React itself, I think asking elsewhere makes more sense.

@deligent-ant
Copy link

the same error to me.

"peerDependencies": {
    "antd": "^3.25.0",
    "styled-components": "^4.4.1",
    "react": "^16.11.0",
    "react-dom": "^16.11.0"
  }
 externals: [
    // nodeExternals(),
    {
      react: {
        root: 'React',
        commonjs2: 'react',
        commonjs: 'react',
        amd: 'react'
      },
      'react-dom': {
        root: 'ReactDOM',
        commonjs2: 'react-dom',
        commonjs: 'react-dom',
        amd: 'react-dom'
      }
    }
}

but still #321 error。

if i don't use useCallback ant useMemo in my npm comoponent,it can work,but i need useMemo...

@ghost
Copy link

ghost commented Apr 16, 2020

If there any one still stucked with this problem,
Please check if you import react like this somewhere:

import React from 'React'

and

import React from 'react'

This causes bundler to import two React modules, as Dan pointed. ( #15050 (comment) )
I spent too much time because of this mistake. 😸

@dmitrirussu
Copy link

dmitrirussu commented May 20, 2020

same problem using component from a custom library... hooks fail

"peerDependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
}

I have done verification of React instace is the same

// Add this in node_modules/react-dom/index.js
window.React1 = require('react');

// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);

@rameshirreplaceable
Copy link

rameshirreplaceable commented Jul 13, 2021

Hi guys, i have fixed this issue please try this out.

Component composition issue react

i really liked its has detailed info about external component related issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants