I'm trying to setup Hot Module Reloading with Webpack 2 and Preact. It's "working", in that it's reloading the entire contents of the app every reload, but I'm getting errors between hot reloads (and I think that's why individual components aren't the only thing reloading).
Here's the relevant parts of my webpack setup:
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.NamedModulesPlugin(),
//etc.
],
entry: [
'webpack-dev-server/client?'+DEV_SERVER,
'webpack/hot/only-dev-server',
'./dev/jsx/index.jsx'
],
devServer: {
hot: true,
inline: true,
contentBase: path.join(__dirname, '/'),
publicPath: '/'
}
My index.jsx file looks like:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './AppProvider.jsx';
const renderApp = () => {
ReactDOM.render(<App/>, document.getElementById('root'));
};
renderApp();
if (module.hot) {
module.hot.accept();
module.hot.accept('./AppProvider.jsx', renderApp);
}
When I make a change in any of the project files, the app contents reload and I get the following errors:
Have any of you gotten this before? I've been Googling all day and haven't found anything...
You're rendering the old AppProvider module, which is null when HMR kicks in. You'll need to move the require()/import for AppProvider.jsx into your HMR handler as shown here.
import React from 'react';
import ReactDOM from 'react-dom';
const renderApp = () => {
let App = require('./AppProvider.jsx');
App = App.default || App; // if you're using ES Modules
ReactDOM.render(<App/>, document.getElementById('root'));
};
renderApp();
if (module.hot) {
module.hot.accept('./AppProvider.jsx', renderApp);
}
Related
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",
})
]
};
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);
I just upgraded to the latest version of React, Webpack, Redux, Babel, etc.
React hot loading works, but there's an error from react-redux: <Provider> does not support changing `store` on the fly.
I noticed that upon reload, it says module App.js needs updating, and then it calls createApp all over again, which creates the store again. I'm not quite sure how to avoid this.
I have over a dozen React apps, so it'd be awesome if I could somehow keep the createApp boilerplate method.
webpack.config.js:
{
devServer: {
hot: true,
host: '0.0.0.0',
},
entry: {
Bundle: __dirname + '/../src/App.js',
},
mode: 'development',
module: {
...
},
output: {
filename: '[name].js',
path: RootDir,
publicPath: 'http://localhost/',
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
resolve: {
alias: {
'react-dom': '#hot-loader/react-dom',
},
},
};
App.js:
import 'react-hot-loader';
import Reducers from './Reducers';
import Routes from './Routes';
import createApp from './createApp';
import { hot } from 'react-hot-loader/root';
const app = createApp(Routes, Reducers);
export default hot(app);
createApp.js
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { applyRouterMiddleware, browserHistory, Router } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { ReduxAsyncConnect } from 'redux-connect';
import configureStore from './configureStore';
import scrollMiddleware from './scrollMiddleware';
export default function createApp(routes, reducers) {
const store = configureStore(reducers);
const history = syncHistoryWithStore(browserHistory, store);
const app = () => routes;
render((
<Provider store={store}>
<Router
history={history}
render={(props) => (
<ReduxAsyncConnect
{...props}
render={applyRouterMiddleware(scrollMiddleware)}
/>
)}>
{app()}
</Router>
</Provider>
), document.getElementById('app'));
return app;
}
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
I want to create a simple application that outputs "Hello, World!" using isomorphic-webpack.
The most simple way is to use the createIsomorphicWebpack high-level abstraction.
First, you need to have have a basic webpack configuration.
webpack configuration does not need to define any special configuration for isomorphic-webpack, e.g. this will work:
import path from 'path';
import webpack from 'webpack';
const webpackConfiguration = {
context: __dirname,
entry: {
'app': [
path.resolve(__dirname, './app')
]
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].js'
},
module: {
loaders: []
}
};
Next, run createIsomorphicWebpack:
import {
createIsomorphicWebpack
} from 'isomorphic-webpack';
createIsomorphicWebpack(webpackConfiguration);
What this does is:
Creates a new webpack compiler.
Runs compiler in watch mode.
Overrides require to use compiled assets.
Therefore, now all you need is to require the entry script:
import {
createServer
} from 'http';
http
.createServer((request, response) => {
response.writeHead(200, {
'Content-Type': 'text/html'
});
response.end(request('./app'));
})
listen(8000);
The entry script must be environment aware, i.e. it must return for node.js process and it must use whatever client specific logic otherwise, e.g.
import React from 'react';
import ReactDOM from 'react-dom';
import style from './style.css';
const app = <div className={style.greetings}>Hello, World!</div>;
if (typeof process === 'undefined' || !process.release || process.release.name !== 'node') {
ReactDOM.render(app, document.getElementById('app'));
}
export default app;
Since this entry script is using css and react, you will need to add the missing loaders to your webpack configuration:
babel-loader and babel-react-preset
css-loader
style-loader