I'm trying to create dll configuration with webpack 4, but i keep getting:
Uncaught ReferenceError: React is not defined
My configuration is very simple:
module.exports = {
entry: {
vendor: ["react", "react-dom"]
},
output: {
filename: "[name]-manifest.dll.js",
path: base.path.project("build"),
library: "[name]",
libraryTarget: "umd"
},
plugins: [
new webpack.DllPlugin({
name: "[name]",
path: base.path.project("build/[name]-manifest.json"),
context: base.path.src("app")
})
]
};
In my development configuration I use the dllreferenceplugin.
new webpack.DllReferencePlugin({
context: base.path.src("app"),
manifest: require("../build/vendor-manifest.json")
})
and of course i define externals in development configuration, because I don't want to include them again when building my development js file:
externals: {
react: "React",
"react-dom": "ReactDOM"
}
In my code I import React.
import * as React from "react";
But in the browser I keep getting React is not defined.
I have googled everything and have not found any solution to this problem?
Thank you for any help!
Use import React, { Component } from 'react'; myself. JSX wants react to be in scope.
Largely only see externals being used when making a library, like a npm module that other folks consume that uses React. React would be marked as an external in that package to avoid double including React in the local application and inside the module.
If this is your app build process, and not an npm module then should not be marking react as externals. Your bundle needs to include react. Marking react as externals excludes it from the bundle and makes it your responsibility to manually load React before the bundle is loaded.
The externals configuration option provides a way of excluding
dependencies from the output bundles.
See the docs on webpack externals here.
Just Remove the external property from your app webpack config file.Also, Include the dll file in your index.html file.
I was also facing the same issue, because I was using both dllReference plugin and external at the same time.
Related
I'm developing a React module locally. For that, I'm linking my module using npm link.
The module is imported successfully but hooks are failing inside the module. It's throwing the following 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.
Checking the suggestions at React docs, I can confirm my app is using duplicate versions of React since the following code returns false:
// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) //false
This issue is full of suggestions but they are confusing. How can I solve it?
Note: Im not breaking rules of hooks, the error appears only when importing the module from an application.
In the module you are developing, add the conflicting packages to peerDependencies (and remove them from dependencies or devDependencies):
// package.json
"peerDependencies": {
"react": "16.13.1",
"react-dom": "16.13.1"
},
Execute npm install in your module.
Now add react and react-dom to the webpack configuration of your module as externals. These packages shouldnt be included in the bundle of the module (the app that uses the module will provide them):
// webpack.config.js
module.exports = {
/*
rest of config...
*/
output: {
filename: "index.js",
pathinfo: false,
libraryTarget: 'umd', // In my case, I use libraryTarget as 'umd'. Not sure if relevant
},
externals: {
// Use external version of React
"react": {
"commonjs": "react",
"commonjs2": "react",
"amd": "react",
"root": "React"
},
"react-dom": {
"commonjs": "react-dom",
"commonjs2": "react-dom",
"amd": "react-dom",
"root": "ReactDOM"
}
},
};
Then, after building your module, in your application you can check that both versions are now the same:
// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) // true :)
Adding react and react-dom as peerDependencies in the package.json didn't work for me.
I had to add an alias to the webpack configuration file:
// webpack.config.js
resolve: {
alias: {
react: path.resolve('./node_modules/react'),
}
In response to another comment, merely moving React to peerDependencies does not adequately resolve the issue in all cases. I would reply to that comment directly, but StackOverflow requires more reputation to respond to wrong answers than it does to post them.
I have a shared React component module built using Webpack and have run into the same issue. I've outlined one possible fix in this comment below which requires modifying peerDependencies and using npm link in a fashion similar to the answer shared by mtkopone.
https://github.com/facebook/react/issues/13991#issuecomment-841509933
My solution is a bit hacky and I wouldn't recommend it for long-term use. If you are using Webpack (which you tagged this question as), this article may detail a more permanent solution (https://medium.com/codex/duplicate-copy-of-react-errors-when-using-npm-link-e5011de0995d). I haven't tried it yet, but the author seems to have tried all the (incorrect) solutions out there and is also running into the hooks issue while trying to build shared component libraries.
The author of that article is trying to debug a Create-React-App app. While CRA uses webpack under the hood, you can't access the webpack.config directly, so the author has to perform some workarounds to do so. If you aren't using CRA, but just plain Webpack, then you could consider using the resolve.alias section of webpack.config to ensure there are no duplicate copies of React (see: https://blog.maximeheckel.com/posts/duplicate-dependencies-npm-link/)
I was attempting to use the peerDependencies and removal of the devDependencies and it was failing.
It turned out I had a node_modules folder in one of the parent folders of the library I was working on and the duplicate version of React was being loaded from there instead of the tool that was trying to use the React library.
Rather than editing the devDependencies to remove react I just wrote a small script to delete anything that's in the peerDependencies from the node_modules folder.
npm view --json=true . peerDependencies | jq -r 'keys | .[] | #text' | while read dep; do rm -r ./node_modules/${dep} && echo Removed ${dep}; done
In my case I was also missing import React from 'react' from couple of files.
check this
I am trying to set up the following architecture: a core React application that gets built with some basic functionality, and the ability to load additional React components at runtime. These additional React components can be loaded on-demand, and they are not available at build time for the core application (so they cannot be included in the bundles for the core application, and must be built separately). After researching for some time, I came across Webpack Externals, which seemed like a good fit. I am now building my modules separately using the following webpack.config.js:
const path = require('path');
const fs = require('fs');
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = {
entry: './src/MyModule.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'MyModule.js',
library: 'MyModule',
libraryTarget: 'umd'
},
externals: {
"react": "react",
"semantic-ui-react": "semantic-ui-react"
},
module: {
rules: [
{
test: /\.(js|jsx|mjs)$/,
include: resolveApp('src'),
loader: require.resolve('babel-loader'),
options: {
compact: true,
},
}
]
},
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx']
}
};
Took a look at the generated MyModule.js file, and it looks correct to me.
Now, in my core app, I am importing the module as follows:
let myComponent = React.lazy(() => import(componentName + '.js'));
where componentName is the variable that matches the name of my module, in this case, "MyModule" The name is not known at build time, and the file is not present in the src folder at build time. To avoid errors from webpack when building this code with an unknown import, I have added the following to my webpack.config.js for the core project:
module.exports = {
externals: function (context, request, callback/*(err, result)*/) {
if (request === './MyModule.js') {
callback(null, "umd " + request);
} else {
callback();
}
}
}
I have confirmed that the function in externals gets called during the build, and the if condition is matched for this module. The build succeeds, and I am able to run my core application.
Then, to test dynamic loading, I drop MyModule.js into the static/js folder where the bundles for my core app live, then I navigate to the page in my core app that requests MyModule via let myComponent = React.lazy(() => import(componentName + '.js'));
I see a runtime error in the console on the import line,
TypeError: undefined is not a function
at Array.map (<anonymous>)
at webpackAsyncContext
My guess is it's failing to find the module. I don't understand where it is looking for the module, or how to get more information to debug.
Turns out that I was making a couple of incorrect assumptions about webpack and dynamic loading.
I was having issues with two things - the kind of module I was loading, and the way that I was loading it.
Dynamic importing is not yet a standard ES feature - it is due to be standardized in ES 2020. This dynamic import will only return a module if the module object you are attempting to load is an ES6 module (aka something that contains an 'export ModuleName'). If you attempt to load something packed up as a CommonJS module, AMD, UMD, the import will succeed, but you will get an empty object. Webpack does not appear to support creating bundles in ES6 format - it can create a variety of module types, and in my config file above, I was actually creating UMD modules (configured via libraryTarget setting).
I had issues with the import statement itself because I was using it within an app bundled by Webpack. Webpack reinterprets the standard ES import statement. Within a standard webpack config (including the one you get from CRA), webpack uses this statement as a split point for bundles, so even modules that are dynamically imported are expected to be there at webpack build time (and the build process will fail if they are not available). I had tried to use webpack externals to tell webpack to load the modules dynamically, which allowed the build to succeed without the modules being there. However, the app still used Webpack's import function instead of the standard JS import function at runtime. I confirmed this by attempting to run import('modulename') from the browser console and getting a different result than my app, which was bundled with webpack.
To solve problem #2, you can tell Webpack to not reinterpret the ES dynamic import by adding some annotation to the import statement.
import(/*webpackIgnore: true*/ 'path/to/module.js');
This will both prevent Webpack from attempting to find and bundle the dynamically imported module at build time, and attempting to import it at runtime. This will make behavior in the app match behavior in the browser console.
Problem #1 was a bit more difficult to solve. As I mentioned above, importing a non-ES6 module will return an empty object (if you await the promise or use .then()). However, as it turns out, the file itself does load and the code gets executed. You can export the module in the "window" format using Webpack, and then load it as follows.
await import(/*webpackIgnore: true*/`path/to/module.js`);
let myModule = window['module'].default;
Another potential solution that avoids using the window object is building the module using a system capable of producing ES6 modules (so, not Webpack). I ended up using Rollup to create an ES6 module that pulled all dependencies into a single file, and ran the output through Babel. This produced a module that loaded successfully via a dynamic ES import. The following was my rollup.config.js (note that I included all external node modules needed in my module - this bloated the module size but is a requirement for my specific application - yours will likely differ and you will need to configure rollup to exclude the modules)
// node-resolve will resolve all the node dependencies
import commonjs from 'rollup-plugin-commonjs';
import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import replace from 'rollup-plugin-replace';
export default {
input: 'src/myModule.jsx',
output: {
file: 'dist/bundle.js',
format: 'esm'
},
plugins: [
resolve(),
babel({
exclude: 'node_modules/**'
}),
commonjs({
include: 'node_modules/**',
namedExports: {
'node_modules/react/index.js': ['Children', 'Component', 'PropTypes', 'PureComponent', 'React', 'createElement', 'createRef', 'isValidElement', 'cloneElement', 'Fragment'],
'node_modules/react-dom/index.js': ['render', 'createElement', 'findDOMNode', 'createPortal'],
'node_modules/react-is/index.js': ['isForwardRef']
}
}),
replace({
'process.env.NODE_ENV': JSON.stringify( 'production' )
})
]
}
I develop a custom library of components in React(16.4) and Webpack 4. Config was eject and import to other project with git+ssh://git#bitbucket.org.... on package.json.
Only one component doesn't work when I used this lib in other project, it's a select input develop with react-select (Jed Watson) and it crashes on click.
All app crashes and I have this error:
Uncaught Error: Minified React error #188; visit https://reactjs.org/docs/error-decoder.html?invariant=188 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
Message from link in error above:
Unable to find node on an unmounted component.
I now my problem is with my webpack config but I don't find any solutions to fix this.
Last hope with community...
I had a similar situation - I am developing a custom component library and importing it into an external project. I had issues with react-bootstrap, react-transition-group, and other external libraries that dynamically mount/unmount DOM elements in production; minified react error #188 would crash production builds. Here's how I solved it in our webpack config under module.exports (both dev and prod configs):
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
"react-native": "react-native-web",
react: path.resolve("./node_modules/react"),
"react-dom": path.resolve("./node_modules/react-dom"),
"react-transition-group": path.resolve(
"./node_modules/react-transition-group"
)
}
This should override any other instances of these particular libraries in all dependencies; anytime they're imported/used, they're forced to use the version that my project dictates in the package.json. Please keep in mind that I'm using Webpack 3, so the fix for Webpack 4 may be different.
Thank you Abi.
I've just resolve it with other way. I add all library in "externals" in my webpack config like this :
externals: [
'react-select': {
root: 'ReactSelect',
commonjs2: 'react-select',
commonjs: 'react-select',
amd: 'react-select',
},
]
I have two modules inside the same directory, managed both by lerna js. One of them is a library that others module includes. Both of them packed by webpack following webpack library authoring.
But, when I launch webpack in the app dir, the process includes all library/node_modules dependencies inside the app, for example vue.js. In library vue is "devDependency" while in the app is "dependencies". This implies two Vue context in navigator. Somebody known why?
Thanks.
You need to add an alias:
module.exports = {
...
....
},
resolve: {
modules: ["node_modules",
alias: {
'vue$': 'vue/dist/vue',
'jquery': 'jquery/dist/jquery.min.js'
}
},
...
Thanks to #evocateur
"Node resolves symlinks when requiring, which means libraries (like React and Vue) that are singletons will break. You need to add resolve.alias webpack config so it always chooses the "root" node_modules packages."
Putting in webpack following works perfectly in resolve.alias:
vue: path.resolve(__dirname, './node_modules/vue/')
I have a couple of React Components in a folder, which is not a react project. The Directory Structure i am following for components is:
~/components/Component1/index.jsx
~/components/Component2/index.jsx
Now I have a React project (built with create-react-app), named "myapp" I want to import those React Components as a package or module in my project.
I have tried mentioning a dependency in my package.json, but gives an error, because I can't mention absolute paths in package.json.
I don't want to publish these components as a npm package.
Kindly help me with this
The problem was:
I was trying to wrap material-ui components like iconButton, iconMenu, etc. to make the components easy to use programatically. To put them into a git repository I need a example directory with a seperate react project using the components I developed. So, I need to develop a package that hold components' definitions and exporting them to be used in other project. I want to keep my implementations private so I cannot even publish it to npm.js.
[you can see the question statement for thorough understanding of thr ptoblem.]
Coming to the solution help me doing the needed, I created a new project with yarn adding minimal dependencies. i.e.
babel
babel-cli
babel-core
babel-loader
babel-preset-es2015
babel-preset-react
babel-preset-stage-2
html-webpack-plugin
raw-loader
webpack
webpack-dev-server
and then devDependencies
react
react-dom
material-ui [occasional]
After the installations, [HERE COMES THE PART] I created a webpack.config.js with following script:
const path = require('path')
const webpack = require('webpack')
module.exports = {
devtool: 'cheap-eval-source-map',
entry: './docs/entry.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
},
resolve: {
alias: {
genericcomponents: path.join(__dirname, 'src')
}
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
}
]
},
devServer: {
contentBase: 'docs/'
}
}
In the code above, after mentioning the output the entry, devtool and output of the transpilation process. I actually have another thing to define, which is alias, I defined the alias with the directory holding the components.
Now this package will hold all the components with the name I provided in the alias parameter.
After that I mentioned loaders for transpilation and file format to jsx, so It will accept the source with JSX Syntax.
And at last I mentioned my the directory where I placed my index.html and entry.js file.
Following is the directory structure of my project:
I have my App.jsx Component in docs folder. which can now import any of the components in the components folder by the giving the package name.
You are welcome to discuss of any of the problem occurred in the above solution.
I know the answer might sound very basic but is there any reason why don't you just copy paste the components into the new app and just use them as any other component ?