React is undefined (Cannot read property 'createElement' of undefined) - reactjs

I'm trying to convert a working ReactJS application into TypeScript, and I've had some issues getting anything to work properly.
import React from "react";
import * as ReactDOM from "react-dom";
import Application from "./Application";
console.log(React); // undefined
ReactDOM.render(
<Application/>, window.document.getElementById("application-wrapper")
);
The console throws an error at <Application />
When I import react like this, react loads:
import * as React from "react";
However, I want to use the import statement using the default export, because I import React using this import syntax in all the existing components:
import React, {Component} from "react";
export default class Whatever extends Component<Props, State> {
...
}
My tsconfig.json file contains this line allowing synthetic defaults:
"allowSyntheticDefaultImports": true
My webpack.config.js file:
let path = require("path");
let config = {
entry: "./src/main.tsx",
output: {
path: path.resolve(__dirname, "build"),
filename: "bundle.js"
},
devtool: "source-map",
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"]
},
module: {
loaders: [
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/
}
]
}
};
module.exports = config;
Not sure what I'm missing here....

Set "esModuleInterop": true instead.

In your typescript configuration i.e tsconfig.json "esModuleInterop": true and "allowSyntheticDefaultImports": true. this will allow you to import CommonJS modules in compliance with es6 modules spec.

Module resolution is a little complicated because Typescript does it different than Babel and Webpack. If you want to know more you can check this comment: https://github.com/Microsoft/TypeScript/issues/5565#issuecomment-155216760
Going back to your problem, allowSyntheticDefaultImports tells Typescript to allow default imports from modules with no default export but the emitted code doesn't change. Because of that, you need to move the responsibility of resolving modules to Webpack or Babel.
To achieve that set moduleResolution module to ES6es2015 in the Typescript config file.
The pipeline will look like this:
TS Code => (TypescriptCompiler) => JS Code with ES6 modules => (Webpack modules resolver) => JS Code

Related

Rollup plugin postcss does not inject css imported from node_modules

Here is my rollup config
// rollup.config.js
const postcss = require('rollup-plugin-postcss');
const autoprefixer = require('autoprefixer');
module.exports = {
rollup(config, _options) {
config.plugins.push(
postcss({
plugins: [
autoprefixer(),
],
extensions: ['.css'],
modules: false,
extract: false,
}),
);
return config;
},
};
So if I import css file local from a relative path, it gets injected but I import from node_modules it doesn't
import React from 'react';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
// The following works if I copy the file locally
// import './ReactToastify.css';
What am I doing wrong here?
I came across exactly the same problem and I think I found the solution. The trick here is to use rollup-plugin-postcss (or also rollup-plugin-styles) in combination with #rollup/plugin-node-resolve.
My rollup.config.js looks something like this:
import { nodeResolve } from '#rollup/plugin-node-resolve'
import postcss from 'rollup-plugin-postcss'
// import styles from 'rollup-plugin-styles'
export default {
...
plugins: [
postcss(),
// styles(),
nodeResolve({
extensions: ['.css']
})
]
};
As far as I can tell, for my simple case, it doesn't matter if you use postcss or styles plugins. Both work the same.

Error resolving module specifier: react while doing dynamic import from API

I am trying to dynamically import react component library from API. The js file is fetched succesfully. The babel transpilation has happened successfully too. If I dynamically import the Dummy.js file from localhost like import Dummy from './components/js/Dummy.js', it works. However API import fails with below error. The same error occurs with react lazy too. I basically want to dynamically load the lib and publish events to it. I am newbie to react and front-end development. Please excuse if this is too silly!.
Error resolving module specifier: react
My App.js
import React, { lazy, Suspense } from 'react';
import './App.css';
import ErrorBoundary from './ErrorBoundary';
class App extends React.Component {
render(){
const loader = () => import( /*webpackIgnore: true*/ `https://example.com/Dummy.js`);
const Dummy = ReactDynamicImport({ loader });
const LoadingMessage = () => (
"I'm loading..."
)
return (
<div className="App">
<h1>Simplewidget</h1>
<div id="simplewidget">
<ErrorBoundary>
<Suspense fallback={LoadingMessage}>
<Dummy />
</Suspense>
</ErrorBoundary>
</div>
</div>
);
}
}
export default App;
rollup.config.js, After multiple attempts I arrived at below configuration https://github.com/jaebradley/example-rollup-react-component-npm-package/blob/master/rollup.config.js
// node-resolve will resolve all the node dependencies
import resolve from '#rollup/plugin-node-resolve';
import babel from 'rollup-plugin-babel';
import commonjs from '#rollup/plugin-commonjs';
import filesize from 'rollup-plugin-filesize';
import localResolve from 'rollup-plugin-local-resolve';
export default {
input: 'src/components/js/Dummy.js',
output: {
file: 'dist/Dummy.js',
format: 'es',
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
},
// All the used libs needs to be here
external: [
'react',
'react-dom'
],
plugins: [
babel({
exclude: 'node_modules/**',
}),
localResolve(),
resolve({
browser: true,
}),
commonjs(),
filesize()
]
}
Dummy.js. I verified in dist/dummy.js that babel transpilation happened correctly. I uploaded the transpiled version to my static hosting site.
import React from "react";
import ReactDOM from "react-dom";
class Dummy extends React.Component {
render() {
return (
<h1>Hello</h1>
);
}
}
export default Dummy;
I have different targets to build, start up my server, create rollup bundle, etc in same app. So, I am pretty confident my rollup doesn't interfere with running the app.
The rollup bundling configuration isn't correct. I was trying to create an es output with commonjs plugin while actually I required an esm module. The error on 'react' is because it is unresolved. Had to make it to use window.React while doing rollup bundle. Also, the App.js should be rolled up as esm bundle to make it dynamically import Dummy.js. In the HTML where app.js's bundle is included, I had to include react and react js umd scripts.
export default {
input: "./src/components/js/Dummy.js",
output: {
file: './dist/Dummy.js',
format: 'esm'
},
plugins: [
babel({
exclude: "node_modules/**"
}),
resolve(),
externalGlobals({
"react": "window.React",
"react-dom": "window.ReactDOM",
})
]
};

React-hot-loader: `hot` could not find the `name` of the the `module` you have provided

I'm using Webpack 4 to create a React project with hooks and I'm trying to get the changes to reload on page live using react-hot-loader following this tutorial.
But I when I try npm start I get following error on the browser:
Error: React-hot-loader: hot could not find the name of the the
module you have provided
This is my App.js contents:
import React from 'react';
import { hot } from 'react-hot-loader';
import Header from './Header';
function App() {
return (
<section className="main">
<Header />
</section>
);
}
export default hot(App);
Alternately I tried importing hot from react-hot-loader/root, but this way I get a different error:
Error: React-Hot-Loader: react-hot-loader/root is not supported on
your system. Please use import {hot} from "react-hot-loader" instead
How could I solve this issue?
You should be requiring it before react:
import { hot } from 'react-hot-loader/root';
import React from 'react';
The package documentation mentions this.
Well, looking at my webpack configs:
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV) }),
],
devServer: {
contentBase: './dist',
hot: true,
},
I had used webpack.HotModuleReplacementPlugin() in plugins and hot: true in devServer which made the second error if I would use react-hot-loader/root.
So removing new webpack.HotModuleReplacementPlugin() from the webpack.config.js, solved my problem.
import { hot } from 'react-hot-loader';
export default hot(module)(App);
or
import { hot } from 'react-hot-loader/root';
export default hot(App);

Loading react component from url

I want to import the react component that I have bundled using web pack.
I am able to complete the task by copying it locally to that folder and then importing it like
import Any from '.dist/index'
and it is working fine.
But what I want to do is uploading this index.js file to somewhere for example Amazon s3. Now I am not able to import the component in the same way as mentioned above.
My webpack.config.js file, I have used to export my bundled component generated by webpack that I am using in another project by copying the index.js and index.css file is
var path = require("path");
var HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index_bundle.js",
libraryTarget: "commonjs2"
},
module: {
rules: [
{ test: /\.(js)$/, use: "babel-loader" },
{ test: /\.css$/, use: ["style-loader", "css-loader"] }
]
},
externals: {
react: "commonjs react"
},
mode: "production",
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
]
};
I want to import the component from file url uploaded to s3.
you can do what you are describing with micro apps. A micro app is basically nothing more than a component that is lazy loaded into the host application from a url at runtime. There is no need to install or import the component at design time. There is a library available that lets you do this with a HOC component.
import React from 'react';
import ReactDOM from 'react-dom';
import MicroApp from '#schalltech/honeycomb-react-microapp';
const App = () => {
return (
<MicroApp
config={{
View: {
Name: 'redbox-demo',
Scope: 'beekeeper',
Version: 'latest'
}
}}
/>
);
});
export default App;
You can find more information on how it works here.
https://github.com/Schalltech/honeycomb-marketplace
This is not the way you should package and deploy your React components. AWS S3 is a bucket for storage of files to serve on the web. It's purpose is not to share code files through projects.
You should publish your React components to a registry such as NPM. After you publish your package to the registry, you should be able to install the package into your app as a dependency by doing something like npm install my_package.

Webpack not bundling node modules imported in dependency JS files

I'm using Webpack to bundle my ReactJS application.
helloworld.js
import React from 'react';
export default class HelloWorld extends React.Component {
render() {
return <h2>Hello {this.props.name}!</h2>;
}
}
index.js
import ReactDOM from 'react-dom';
import HelloWorld from './helloworld';
ReactDOM.render(<HelloWorld name="World" />,
document.getElementById('container'));
webpack.config.js
module.exports = {
entry: './index.js',
output: 'bundle.js',
module: {
loaders: [
{
test: /(\.js|\.jsx)/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
]
}
};
When I run webpack-dev-server --progress --colors I'm getting an error in the browser "React is not defined" but if I import React directly in the index.js then the error not comes. Why Webpack is not including React in the bundle if it is referenced in the helloworld.js file?
Well webpack only tries to bundle up the individual modules by reading the dependencies in it and resolving them to render a particular element. Now while bundling
ReactDOM.render(<HelloWorld name="World" />,
document.getElementById('container'));
ReactDOM tries to execute React.createElement(_Helloworld2.default, { name: 'World' }), document.getElementById('app') function which requires React as a dependency that is not present in your index.js file so it gives an error and solve the issue when you import React in your index.js file. I hope I was able to explain and my answer helps you.
You are missing import React from 'react'; statement.
You will need it every time You write some JSX in a file, because it is transformed into React.createElement(..) function calls.

Resources