React-hot-loader: Purpose of using module.hot.accept("./App") with a single HMR entry point

Created on 11 Nov 2016  路  13Comments  路  Source: gaearon/react-hot-loader

I'm trying to find out what is the purpose of using:

render(<AppContainer><App /></AppContainer>, document.getElementById('a'));
if(module.hot) {
    module.hot.accept("./App", () => {
        const NextApp = require('./App').default;
        render(<AppContainer><NextApp /></AppContainer>, document.getElementById('a'));
    });
}

over

render(<AppContainer><App /></AppContainer>, document.getElementById('a'));
if(module.hot) {
    module.hot.accept();
}

both solutions work for me exactly the same (note: I've not tested them extensively). I'm using "react-hot-loader": "^3.0.0-beta.6", and webpack-dev-server --hot --inline if that's relevant.

question

Most helpful comment

The difference is that in case of

module.hot.accept("./App", () => {
    const NextApp = require('./App').default;
    render(<AppContainer><NextApp /></AppContainer>, document.getElementById('a'));
});

only _accept callback_ will be executed on each hot loading.

In case of

module.hot.accept()

whole js file will be executed each time, not just _accept callback_.

All 13 comments

Yeah that works well in many cases. It might be worth changing the docs to use that as the default example.

However if you have separate module.hot.accept calls in other non-entry modules (reloading Redux reducers, for example), it'll break things. I'm not exactly sure why, but I think by not giving the specific dependency (App.js), HMR will try to get updates from all other modules and override/ignore their module.hot.accept.

Yes, module.hot.accept() will stop the hot reloading propagation.

Yes, module.hot.accept() will stop the hot reloading propagation.

That is either not the case, or the chain is starting from the .accept() calls that use a dependency.

You can try to clone my repo at https://github.com/btmpl/react-hot-loader-example and:

  • npm install
  • npm start
  • open the browser
  • make a change to src/reducers/reducer.js
  • the change will be reflected

The problem I'm running into right now is that making change to any of the components will now break the Provider - it will try to swap the store even though it wasn't updated. This will happen in both cases - when (in index.js) I call module.hot.accpet() and when I use module.hot.accept('./components/App', () => { ...

@BTMPL: I only scanned the example briefly, but it looks like you're recreating the store on every re-render. You'll need to pull the store initialization out of the component so it only happens once, like the example I linked to in my previous comment.

@calesce you are correct, such a silly mistake. I moved let store = createStore(testReducer); outside of the exported function and it works perfect right now.

The original question remains though:

I can change

if(module.hot) {
    module.hot.accept('./components/App', () => {
        console.log("index.js HMR");
        const NewApp = require('./components/App').default;
        render(<AppContainer><NewApp /></AppContainer>, document.getElementById('a'));
    });
}

to

if(module.hot) {
    module.hot.accept();
    console.log("index.js HMR");
    const NewApp = require('./components/App').default;
    render(<AppContainer><NewApp /></AppContainer>, document.getElementById('a'));
}

and the end results are exactly the same. Can someone point me to a use-case where it is required to pass the dependency and use the first version of the code?

So that example will break if you modify code in store.js. But otherwise, it works pretty well. I'd guess that it's possible for most use cases, but you'll have to remember to keep references to stateful objects, like the Redux store.

Ok, good enough. I'll close the issue and maybe revisit it if I run into something breaking ;)

Cool, it's definitely a nice approach I didn't know was possible 馃槃

The difference is that in case of

module.hot.accept("./App", () => {
    const NextApp = require('./App').default;
    render(<AppContainer><NextApp /></AppContainer>, document.getElementById('a'));
});

only _accept callback_ will be executed on each hot loading.

In case of

module.hot.accept()

whole js file will be executed each time, not just _accept callback_.

@Diokuz - thanks, I can see this happening in my code, so it is a valid use case.

I can't get the module.hot.accept(dep, callback) way to work at all, it does not seem to recognize the paths I pass. However, module.hot.accept() works, but I don't think react-hot-loader is doing it's magic in that case - I could nicely remove the AppContainer component without any changes? Also I get this annoying error where react-redux claims the store is changed (there should only be one, or it is recreated as my module is reloaded).

I also cannot figure out why the path-specific module.hot.accept thing is not working, anyone know how to debug that kind of issues?

UPDATE: I had an old version of the webpack-hot-middleware, this was most likely the cause of my problems.

Same problem as @tvedtorama, except that I _do_ have the most recent version of webpack-hot-middleware.

With the path specific version I invariably get "full reload required" errors like the following

[HMR] The following modules couldn't be hot updated: (Full reload needed)
This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. See http://webpack.github.io/docs/hot-module-replacement-with-webpack.html for more details.

  • ./js/components/container/HomePage.jsx

Babel config looks like this, which appears to be correctly disabling the es2015 modules.

{
  "sourceMaps": "inline",
  "presets": [
    "flow",
    [
      "latest",
      {
        "es2015": {
          "modules": false
        }
      }
    ],
    "stage-2",
    "react"
  ],
  "plugins": [
    "transform-class-properties",
    "transform-object-assign",
    "transform-es2015-block-scoping",
    "transform-runtime",
    "react-hot-loader/babel"
  ],
  "env": {
    "production": {
      "plugins": [
        "transform-react-remove-prop-types",
        "transform-react-constant-elements"
      ]
    }
  },
  "babelrc": false
}

using a single HMR entry point used to work for me until I upgraded react-router. I solved the issue by adding the routes to the updating dependencies

module.hot.accept(["./App", "./routes"], () => { render(...) })
Was this page helpful?
0 / 5 - 0 ratings

Related issues

adesmet picture adesmet  路  4Comments

ghost picture ghost  路  3Comments

esturcke picture esturcke  路  3Comments

theKashey picture theKashey  路  4Comments

sandysaders picture sandysaders  路  4Comments