Skip to content

Hooks error from external component #15050

Closed
@mrbinr

Description

@mrbinr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions