React-hot-loader: Full page reload instead of module reload

Created on 22 Nov 2016  Â·  21Comments  Â·  Source: gaearon/react-hot-loader

Description

What you are reporting:

Every time something is changed inside a react component, the entire application is reloaded (full page reload).

Expected behavior

What you think should happen:
Only the changed module should update

Environment

React Hot Loader version: 3.0 beta 6

Run these commands in the project folder and fill in their results:

  1. node -v: 6.9.1
  2. npm -v: 3.10.9

Then, specify:

  1. Operating system: MacOS
  2. Browser and version: Chrome 54

Reproducible Demo

None at the moment.
With one project, it seems it reloads every time. And I can see this error before the refresh:
screen shot 2016-11-21 at 8 32 37 pm

With another project, depending on what changes, I can also have a full reload. Usually, I'm never expecting a full reload, especially if only a string changes. So I wonder what could cause this error

Cannot apply update. Need to do a full reload. 
Error: aborted because ./src/X.js is not accepted
Update propagation: ./src/X.js -> ./src/Y.js -> 2
bug

Most helpful comment

my problem was that I put the following code in the component rather than index.js
import injectTapEventPlugin from 'react-tap-event-plugin'; injectTapEventPlugin();
after putting it in index.js problem was gone
I found out by turning on Preserve log in chrome devtools thanks to @sbussetti

All 21 comments

I'm seeing the same thing as well.

Version

Node: 6.9.1
NPM: 3.10.8
react-hot-loader: 3.0.0-beta.6

Screenshot

screen shot 2016-11-22 at 10 03 18 am

Problem did not occur after downgrading to 1.3.0

Hi, I can't take a look at either of you issues without at least some code to look at.

I have the same problem like mkalish since upgrading from 1.3.0 to 3.0.0-beta6. Always full reload with the same error message. With 1.3.0 hot module update was still working.

I'm using react-hot-loader/patch as Webpack entry point. Problem occurs no matter if i use react-hot-loader/babel plugin or react-hot-loader/webpack loader.

Am I getting it right, that I don't have to call module.hot.accept by hand if I use the react-hot-loader/patch Webpack entry point?

This is my startup code:

import "babel-polyfill";
import "isomorphic-fetch";
import * as React from "react";
import * as injectTapEventPlugin from "react-tap-event-plugin";
import * as ReactDOM from "react-dom";
import {MainContainer} from "./parts/main";
import {createStore, applyMiddleware} from "redux";
import {Provider} from "react-redux";
import * as createLogger from "redux-logger";
import thunkMiddleware from "redux-thunk";
import {MainReducer} from "./common-reducers/main-reducer";
import {StyleRoot} from "radium";
import {AppContainer} from "react-hot-loader";

injectTapEventPlugin();
let store:any = createStore(MainReducer.mainState, applyMiddleware(thunkMiddleware, createLogger()));

ReactDOM.render(
    <AppContainer>

        <Provider store={store}>
            <StyleRoot>
                <MainContainer/>
            </StyleRoot>
        </Provider>

    </AppContainer>,
    document.getElementById('app')
);

@calesce I was struggling with this problem for a couple days and came across this article that helped me solve my problem

https://medium.com/@rajaraodv/webpacks-hmr-react-hot-loader-the-missing-manual-232336dc0d96#.7kq8oibt5

Important: Use either CLI or Config file but never mix and match the above two ways.

I had followed the migration steps from 1.x to 3,
but the eureka moment was after I moved my CLI params (--inline and --hot) into my webpack.config file, and then everything fell into place

What do you mean with "I had followed the migration steps from 1.x to 3"? The link you posted doesn't contain that.

I still don't have success. Always full reload with the same error message. :(

This is my webpack config (react-hot-loader/babel is added as plugin in .babelc):

const webpack = require('webpack');
const WebpackConfig = require('webpack-config');
const path = require('path');
const buildPath = path.resolve(__dirname, '../www');
const srcPath = path.resolve(__dirname, '../src');
const testPath = path.resolve(__dirname, '../test');

const config = new WebpackConfig().merge({
    //Entry points to the project
    entry: {
        app: [
            "react-hot-loader/patch",
            "babel-polyfill",
            "./src/app/app.tsx"
        ],
        devServer: [
            'webpack/hot/only-dev-server'
        ]
    },
    //Server Configuration options
    devServer: {
        contentBase: 'src/www',  //Relative directory for base of server
        hot: true,        //Live-reload
        port: 3000,        //Port Number
        host: 'localhost',  //Change to '0.0.0.0' for external facing server
        inline: true,

        //Map API calls to local backend server
        proxy: {
            '/api/*': {
                target: 'http://localhost:8080'
            }
        }
    },
    plugins: [
        //Enables Hot Modules Replacement
        new webpack.HotModuleReplacementPlugin(),
    ],
    //Config options on how to interpret requires imports
    resolve: {
        extensions: ["", ".js", ".jsx", ".ts", ".tsx"],
        //node_modules: ["web_modules", "node_modules"]  (Default Settings)
    },
    output: {
        path: buildPath,    //Path of output file
        filename: '[name].js',
    },
    module: {
        loaders: [
            {
                test: /\.ts$|\.tsx$/,
                loaders: ['babel-loader', 'ts-loader'],
                include: [srcPath, testPath],
            },
            {
                test: /\.css$/,
                loader: "style-loader!css-loader"
            },
        ],
    },
});

module.exports = config;

@Q-Man I updated my comment with a link to the migration example

.babelrc

{
  "presets":[
    "es2015",
    "react"
  ],
  "plugins": [
    "react-hot-loader/babel",
    "transform-object-rest-spread"
  ]
}

webpack.config.js

module.exports = {
  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:9000',
    'webpack/hot/only-dev-server',
    './client/index.jsx'
  ],
  output: {
    path: '/client',
    filename: 'bundle.js',
  },
  devtool: 'source-map',
  devServer: {
    hot: true,
    contentBase: './client',
    inline: true,
    port: 9000,
    proxy: {
      '/**': {
        target: 'http://localhost:9001',
        bypass: function ( req, res, proxyOptions ) {
          if ( req.path === '/' ) return '/index.html'
          else return false
        },
      }
    }
  },
  resolve: {
    root: path.resolve('.'),
    extensions: ['', '.js', '.jsx'],
  },
  module: {
    loaders: [
      {
        test: /\.scss$/,
        loaders: ["style","css","sass"],
      },
      {
        test: /\.(png|woff|woff2|eot|ttf|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'url',
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'babel',
      }
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ]
}

index.jsx

import React from 'react'
import ReactDOM from 'react-dom'
import Root from './containers/Root'
import { AppContainer } from 'react-hot-loader'

render( Root )

// Hot Module Replacement API
if (module.hot) {
  module.hot.accept('./containers/Root', () => {
    const NextRoot = require('./containers/Root').default;
    render( NextRoot )
  })
}

function render ( RootElement ) {
  ReactDOM.render(
    <AppContainer>
      <RootElement/>
    </AppContainer>,
    document.querySelector('#root')
  )
}

containers/Root/index.jsx

import "client/styles/index.scss"

import React from 'react'

import { createStore, applyMiddleware } from 'redux'
import { connect, Provider } from 'react-redux'
import rootReducer from './reducer'
import thunk from 'redux-thunk'

import {
  Route,
  Router,
  IndexRoute,
  hashHistory,
} from 'react-router'

import App from 'client/containers/App'
import Index from 'client/containers/Index'
import Learn from 'client/containers/Learn'
import Review from 'client/containers/Review'
import Designer from 'client/containers/Designer'
import Inspector from 'client/containers/Inspector'

const initialState = undefined
export const store = applyMiddleware( thunk )( createStore )( rootReducer, initialState, window.devToolsExtension && window.devToolsExtension() )

const Root = props => {
  return (
    <Provider store={ store }>
      <Router history={ hashHistory }>
        <Route path="/" component={ App }>
          <IndexRoute component={ Index }/>
          <Route path="/learn" component={ Learn }/>
          <Route path="/designer" component={ Designer }/>
          <Route path="/inspector" component={ Inspector }/>
          <Route path="/review" component={ Review }/>
          <Route path="*" component={ Index }/>
        </Route>
      </Router>
    </Provider>
  )
}

export default Root

Thanks, @thomfoolery. It'd be nice if the author could instead help with Webpack's/our docs instead of Medium articles :smile:

@Q-Man / @mkalish: please open a separate issue with a repository displaying the issue if you're still having the same problem. This issue already has three different people with potentially different problems.

Thanx @thomfoolery, your code helped me to find the problem.

The main reason for my problem was this: I'm using HMR with TypeScript. Because there is no typing for "module.hot" I tried to avoid the TypeScript compile error by writing the HMR replacement code like this:

//NOT WORKING!!!
if (module["hot"]) {
    module["hot"].accept('./parts/main', () => {
        const NextRoot = require('./parts/main').MainContainer;
        render( NextRoot )
    })
}

This for some reason this doesn't work. So I changed it to this and now it works:

//WORKING!!!
declare var module; //avoid TypeScript compile error
if (module.hot) {
    module.hot.accept('./parts/main', () => {
        const NextRoot = require('./parts/main').MainContainer;
        render( NextRoot )
    })
}

So i've got a very similar issue http://th3fallen.d.pr/4o2K in that video you see the word coworker become coworkers as expected but then the entire application reloads is this similar to what you were seing?

So I'm still having the same issue... In your post @thomfoolery, why are you adding 'webpack/hot/only-dev-server',? It seems it adds a second dev-servers. I can see twice this log in the console with that:

Waiting for update signal from WDS...

I wonder if my initial issue might be related to react-router 3 as well..
How do you make HR works with react router?

@calesce Do you have any example with RR? It looks like most people have issues with that specifically

Any updates on this?

still getting it here, haven't found a fix

@gaearon is there any kind of verbose logging we can enable (via config or source build) that would give us a better idea of why our stacks are insisting on doing full reloads? I just spent two hours trying to isolate the cause and came up with nothing =/ I'm not even sure if react-router has anything to do with it!

EDIT: Update. Sigh. For posterity's sake, here is what I was doing wrong and what I had to fix. I'm using webpack 2, babel, es6, etc in my project.

  1. Did not enable {devServer: {hot: true}} in my webpack.config.js
  2. Did not disable the babel modules feature, "presets": [ ["es2015", {"modules": false}] ... ]
  3. Failed to appropriately configure my entry point's hot.accept call. I had:
  if (module.hot) {
    module.hot.accept('./App.jsx', (App) => { render(App) })
  }

when I should've had

  if (module.hot) {
    module.hot.accept('./App.jsx', () => { render(App) })
  }

In the former, I was receiving an array of strings naming the matched component, but using es6 modules I did not need that.

Fundamentally the stupid thing I forgot to do that helped me solve everything was turn on "preserve log" in my console. =/ Per my initial comment, there is, in fact, a wealth of warnings explaining what is wrong if you don't let the console reset on reload.

I am experiencing this, and can echo @tleunen, I am seeing this:

[HMR] Waiting for update signal from WDS...      dev-server.js:49 
[HMR] Waiting for update signal from WDS...      only-dev-server.js:66 
[WDS] Hot Module Replacement enabled.            client?843a:41 

I can't find anything else that's different from what others are doing. I am not using React Router.

my problem was that I put the following code in the component rather than index.js
import injectTapEventPlugin from 'react-tap-event-plugin'; injectTapEventPlugin();
after putting it in index.js problem was gone
I found out by turning on Preserve log in chrome devtools thanks to @sbussetti

@sbussetti your #2 is what was causing my issue, thanks for writing it down. I recommend anyone struggling with this to go to the boiler plate as well and comparing your configuration.

I'm running into this problem with [email protected] too.

Does anyone have a link to configuring HMR with [email protected] and Webpack 3?

Edit: I solved my problem — I was following these docs closely, but not webpack-dev-server's docs closely enough.

It is recommended that devServer.publicPath is the same as output.publicPath.

Setting both devServer.publicPath and output.publicPath to /js/ worked for my configuration (with both 1.3.1 and 3.0-beta).

It should be solved in v4 please give it a try!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mtscout6 picture mtscout6  Â·  3Comments

mqklin picture mqklin  Â·  3Comments

Opty1712 picture Opty1712  Â·  4Comments

rockchalkwushock picture rockchalkwushock  Â·  3Comments

reintroducing picture reintroducing  Â·  4Comments