Reputation: 2488
I have a simple component like this.
import React, {useState} from 'react';
function MyComponentWithState(props) {
const [value, setValue] = useState(0);
return (
<p>My value is: {value}</p>
)
}
export default MyComponentWithState;
and I want to publish it on NPM as a separate package. so, to do that I prepared package.json and webpack.config.js like below.
package.json:
{
"name": "try-to-publish",
"version": "0.0.1",
"description": "Just a test",
"main": "build/index.js",
"scripts": {
"start": "webpack --watch",
"build": "webpack"
},
"author": {
"name": "Behnam Azimi"
},
"license": "ISC",
"peerDependencies": {
"react": "16.9.0",
"react-dom": "16.9.0"
},
"dependencies": {
"react": "16.9.0",
"react-dom": "16.9.0",
"prop-types": "15.7.2",
"react-scripts": "3.1.1",
"webpack": "4.39.3"
},
"devDependencies": {
"@babel/core": "7.6.0",
"@babel/plugin-proposal-class-properties": "7.5.5",
"@babel/preset-env": "7.6.0",
"@babel/preset-react": "7.0.0",
"babel-loader": "8.0.6",
"babel-plugin-transform-object-rest-spread": "6.26.0",
"babel-plugin-transform-react-jsx": "6.24.1",
"css-loader": "3.2.0",
"node-sass": "4.12.0",
"sass-loader": "8.0.0",
"style-loader": "1.0.0",
"webpack-cli": "3.3.8",
"webpack-external-react": "^1.1.2"
}
}
webpack.config.json:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'index.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(__dirname, 'src'),
use: {
loader: "babel-loader"
}
},
]
},
resolve: {
alias: {
'react': path.resolve(__dirname, 'node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),
}
},
externals: {
'react': "commonjs react",
'react-dom': "commonjs react-dom"
},
};
and here is my .babelrc:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
These configs work like charm when I publish my component to NPM and install it in my another ReactJs project with `npm install , but my point is the local test!
I want to test this component/lib before publish. To do this I use npm link
feature to link my component with my main ReactJS project.
As you saw above, my component is functional and I used hooks too. So when I inject the locally linked lib to my main ReactJs project face this 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
My issue is related to the 3td reason. My project uses ReactJs and import it once and also my component will import React! I mean twice React import in one project!.
I also have externals config about react
and react-dom
in my Webpack config.
What should I do to solve that? Where is my mistake?
Update:
I also tried what @sung-m-kim and @eddie-cooro say but it not worked! Mean, I change the package.json and removed react
and react-dom
from dependencies
and add them to devDpendencies
.
Upvotes: 15
Views: 18309
Reputation: 578
I also had the same issue. In my case, I developed some UI components as a package, and inside that I have an example folder with the React app which Create React App.
The issue is when I install the package into an example app using npm i ../
it installs all the files in the package to the example app including the node_modules folder. Since I have installed the react
and react-dom
as peer dependencies, the example app now has two different react copies.
Removing the node_module folder from the package and re-installing the package again fixed my issue.
Upvotes: 1
Reputation: 1968
Set the react
and react-native
packages only inside of the peerDependencies
part of package.json, not the dependencies
. Also for local development (When your package is not included in any other react projects and you want to to run it locally), you can use the devDependencies
field.
Upvotes: 3
Reputation: 1496
I finally solved this problem by these steps.
run npm link inside
<your-library-package>/node_modules/react
also
run npm link inside
<your-library-package>/node_modules/react-dom
then run npm link react and npm link react-dom inside your application root directory
and dont forget to keep react and react-dom as externals in the library
// webpack.config.js
const externals = {
"react": "react",
"react-dom": "react-dom",
}
module.exports = {
.
.
.
externals
}
Upvotes: 11
Reputation: 509
I resolve this problem in a typescript react project.
probably, when use the npm link
use the react
from main app project and the component project.
So, in your package.json
remove react
from dependencies
and/or devDependencies
Check the answer: https://stackoverflow.com/a/62807950/5183591
Upvotes: 1
Reputation: 2488
I solved my issue. I used RollupJS instead of Webpack for bundling as bundle tool.
Here is my rollup.config.js:
import {uglify} from 'rollup-plugin-uglify'
import babel from 'rollup-plugin-babel'
export default {
input: "./src/index.js",
external: ['react', 'react-dom'],
output: {
name: 'test-lib',
format: "cjs",
},
plugins: [
babel({
exclude: "node_modules/**"
}),
uglify(),
],
};
and my package.json:
{
"name": "test-lib",
"version": "1.0.0",
"main": "dist/test-lib.min.js",
"scripts": {
"build": "rollup -c -o dist/test-lib.min.js"
},
"author": "Behnam Azimi",
"license": "ISC",
"peerDependencies": {
"react": "^16.9.0",
"react-dom": "^16.9.0"
},
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"@babel/preset-react": "^7.0.0",
"rollup": "^1.21.4",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-uglify": "^6.0.3"
}
}
After these changes, npm link
worked truly in my ReactJS (Hooks) project.
Notice that it's just a simple Rollup config to show my solution and you can add many kinds of stuff like hot reloading, styles loaders, and many other plugins to the config.
Upvotes: 2