Replacing a hot module - updating, but not re-rendering

I am running an express server that will act as an API for my React application, which comes bundled and served by a webpack-dev server.

I try to get a hot swap module to work, and almost there, when I make changes to my files, I get this in the console:

HMR console output

But the application never redraws unless it is updated manually. I don’t know if this is relevant, but when I update my .scss files, it is updated unnecessarily manually and updates as I expected.

Versions:

"webpack": "2.1.0-beta.22"

"webpack-dev-server": "2.1.0-beta.8"

"react-hot-loader": "3.0.0-beta.5"

I tried the last web package, but it gave me validation errors that could not be overcome.

I run webpack through: "webpack": "webpack-dev-server --port 4000 --env.dev" , and my express server runs on http://localhost:3000 .

Here is my webpack.config.babel.js :

 const webpack = require('webpack'); const { resolve, join } = require('path'); const { getIfUtils, removeEmpty } = require('webpack-config-utils') const getEntry = (ifDev) => { let entry if (ifDev) { entry = { app: [ 'react-hot-loader/patch', 'webpack/hot/dev-server', 'webpack-dev-server/client?http://localhost:4000/', './js/index.js' ], vendor: ['react'] } } else { entry = { bundle: './js/index.js', vendor: ['react'] } } return entry } const config = env => { const { ifProd, ifDev } = getIfUtils(env) return { entry: getEntry(ifDev), output: { path: resolve('./public/dist/'), publicPath: 'http://localhost:4000/', filename: '[name].bundle.js', }, context: resolve(__dirname, 'assets'), devtool: env.prod ? 'source-map' : 'eval', devServer: { contentBase: resolve('./public/dist/'), headers: { 'Access-Control-Allow-Origin': '*' }, publicPath: 'http://localhost:4000/', hot: true, noInfo: true, inline: true }, bail: env.prod, module: { loaders: [ { test: /\.scss$/, loaders: [ 'style', 'css', 'sass' ], exclude: /node_modules|lib/ }, { test: /\.(js|jsx)$/, exclude: /node_modules/, loaders: [ 'babel-loader' ] }, { test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, loader: 'file-loader' } ] }, resolve: { extensions: ['.js', '.jsx'] }, plugins: removeEmpty([ ifDev(new webpack.NoErrorsPlugin()), ifDev(new webpack.NamedModulesPlugin()), ifDev(new webpack.HotModuleReplacementPlugin()), new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify((env.prod) ? 'production' : 'development') } }), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: Infinity, filename: 'vendor.bundle.js' }), ifProd(new webpack.LoaderOptionsPlugin({ minimize: true, debug: false })), ifProd(new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, output: { comments: false }, sourceMap: false })) ]), } } module.exports = config 

Here is my .babelrc where I call react-hot-loader

 { "presets": [["es2015", { modules: false }], "stage-0", "react"], "plugins": ["react-hot-loader/babel"], "env": { "test": { "plugins": ["istanbul"], "presets": ["es2015", "stage-0", "react"] } }, "sourceMaps": "inline" } 
+7
javascript webpack webpack-dev-server hot-module-replacement
source share
2 answers

With React Hot Loader v3 and Babel transform, you want to do this at the root of your component (where you render or where you create the Redux provider):

 render( <AppContainer> <Root store={ store } /> </AppContainer>, document.getElementById('root') ); if (module.hot) { module.hot.accept('./containers/Root', () => { const RootContainer = require('./containers/Root').default; render( <AppContainer> <RootContainer store={ store } /> </AppContainer>, document.getElementById('root') ); }); } 

In the new version of Hot Loader, you must explicitly accept the hot update using module.hot.accept .

In a more complex Redux project (with routing and hot reload reducers) you can do something like this:

 /** * Starts the React app with the Router, and renders it to the given DOM container * @param {DOMElement} container */ export default function app(container) { const store = createStore( combineReducers({ ...reducers, routing: routerReducer, form: formReducer, }), compose( applyMiddleware( routerMiddleware(hashHistory), thunkMiddleware, promiseMiddleware ), process.env.NODE_ENV !== 'production' && window.devToolsExtension ? window.devToolsExtension() : (param) => param ) ); if (module.hot) { module.hot.accept('./reducers', () => { const nextReducers = require('./reducers'); const nextRootReducer = combineReducers({ ...nextReducers, routing: routerReducer, form: formReducer, }); store.replaceReducer(nextRootReducer); }); } const history = syncHistoryWithStore(hashHistory, store); render({ store, history, container }); store.dispatch(loadEventsWhenLoggedIn()); if (module.hot) { module.hot.accept('./render', () => { const newRender = require('./render').default; newRender({ store, history, container }); }); } } 

(and render.js)

 /** * Starts the React app with the Router, and renders it to the given DOM container * @param {DOMElement} container */ export default function render({ store, history, container }) { ReactDOM.render( <Provider store={store}> <div className='container'> <Routes history={history} store={store} /> </div> </Provider>, container ); } 

For more examples, you should take a look at the repo example, for example, Dan Abramov Redux devtools, for example, this file: https://github.com/gaearon/redux-devtools/blob/master/examples/todomvc/index.js

+4
source share

HMR automatically works for CSS, because style-loader supports this ready-made option.

This is not the case with React. You need react-hot-loader .

Install it using npm and change this:

 { test: /\.(js|jsx)$/, exclude: /node_modules/, loaders: [ 'babel-loader' ] }, 

To:

 { test: /\.(js|jsx)$/, exclude: /node_modules/, loaders: [ 'react-hot-loader', 'babel-loader' ] }, 

If you want to know more, I recommend reading the Webpacks HMR and React-Hot-Loader - Missing Guide .

+2
source share

All Articles