webpack / webpack-dev-server

Serves a webpack app. Updates the browser on changes. Documentation https://webpack.js.org/configuration/dev-server/.
MIT License
7.78k stars 1.43k forks source link

Hot update not update the view (screen) #100

Closed rottmann closed 9 years ago

rottmann commented 9 years ago

I wrote a little react app and serve static files from a separate express server (to redirect all sub-pathes to index.html [react-router]).

static server on port 8080 webpack hot dev server on port 8081

Browser console log:

[HMR] Waiting for update signal from WDS...
client:14 [WDS] Hot Module Replacement enabled.

after modify and save a file (e.g. router.js)

client:18 [WDS] App updated. Recompiling...
client:60 [WDS] App hot update...
dev-server.js:54 [HMR] Checking for updates on the server...
dev-server.js:34 [HMR] Updated modules:
dev-server.js:36 [HMR]  - 11
dev-server.js:40 [HMR] App is up to date.

But on the screen nothing change

The cli console log show:

Hash: a00a156bb0c50980187c
Version: webpack 1.5.3
Time: 483ms
                               Asset     Size  Chunks             Chunk Names
                              app.js   130429       0  [emitted]  app
                           vendor.js  1854993       1  [emitted]  vendor
0.919fa51221ca3cdacfb2.hot-update.js     3980       0  [emitted]  app
919fa51221ca3cdacfb2.hot-update.json       36          [emitted]  
chunk    {0} app.js, 0.919fa51221ca3cdacfb2.hot-update.js (app) 117673 {1} [rendered]
    [0] multi app 52 {0}
    [1] (webpack)-dev-server/client?http://localhost:8081 1674 {0}
    [2] ./src/app.js 2597 {0} [1 error]
    [4] (webpack)/hot/dev-server.js 2916 {0}
   [11] ./src/router.js 3370 {0} [built]
   [34] (webpack)-dev-server/client/web_modules/socket.io/index.js 1149 {0}
   [89] (webpack)-dev-server/client/web_modules/socket.io/socket.io.js 105915 {0}
chunk    {1} vendor.js (vendor) 1601853 [rendered]
    [0] multi vendor 76 {1}
.
.
.
  [257] ./~/react/lib/toArray.js 3176 {1} [built]
webpack: bundle is now VALID.

Structure

|- build/
|- dev/
   |- webpack.base.js
   |- dev-server.js
|- src/
   |- app.js
   |- router.js
|- static/
   |- index.html
   |- assets/
      |- ...some files...

package.json

  "dependencies": {
    "react": "^0.12.2",
    "react-router": "^0.11.4"
  },
  "devDependencies": {
    "express": "^4.11.1",
    "jsx-loader": "^0.12.2",
    "react-hot-loader": "^1.1.1",
    "webpack": "^1.5.3",
    "webpack-dev-server": "^1.7.0"
  }

webpack.base.js

var webpack = require('webpack');
var path = require('path');

var vendorLibs = [
    'react',
    'react-router'
];

module.exports = function(options) {
    return {
        context: path.join(__dirname, '../', 'src'),
        entry: {
            app   : [
                'webpack-dev-server/client?http://localhost:8081',
                'webpack/hot/dev-server',
                './app.js'
            ],
            vendor: vendorLibs,
        },
        output: {
            path             : path.join(__dirname, '../', 'build'),
            filename         : '[name].js',
            chunkFilename    : '[id].bundle.js',
            sourceMapFilename: '[file].map',
            pathinfo         : true,
            publicPath       : 'http://localhost:8081/'
        },
        module: {
            loaders: [
                { test: /\.js$/, loaders: ['react-hot', 'jsx?harmony'] }
            ]
        },
        resolve: {
            extensions: ['', '.js']
        },
        debug: options.debug,
        devtool: options.devtool,
        plugins: [
            new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: Infinity }),
            new webpack.HotModuleReplacementPlugin()
        ]
    }
};

dev-server-js

var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var express = require('express');
var path = require('path');
var fs = require('fs');

var app = express();
var config = require('./webpack.base.js')

// Redirect all non existing files to index.html
app.get('/*', function(req, res) {
    var filename = path.join(__dirname, '../', 'static', req.url);
    if (fs.existsSync(filename)) {
        console.log('static: ' + req.url);
        res.sendFile(filename);
    } else {
        console.log('static: index.html (' + req.url + ')');
        res.sendFile(path.join(__dirname, '../', 'static') + '/index.html');
    }
});

var compiler = webpack(config({
    devServer    : true,
    devtool      : 'eval',
    debug        : true
}));

var server = new WebpackDevServer(compiler, {
    contentBase: 'http://localhost:8081',
    hot: true,
    quiet: false,
    noInfo: false,
    lazy: false,
    watchDelay: 300,
    publicPath: 'http://localhost:8081/',
    stats: { colors: true },
});

server.listen(8081, 'localhost', function() {});
app.listen(8080);

app.js

<!DOCTYPE html>
<html>
<body>
    <div id="app"></div>
    <script src="http://localhost:8081/vendor.js"></script>
    <script src="http://localhost:8081/app.js"></script>
</body>
</html>

Is it a problem when serving the index.html from an other port? The docs say it is possible (http://webpack.github.io/docs/webpack-dev-server.html#combining-with-an-existing-server)

sokra commented 9 years ago

The react-hot-loader only works for React components. Is route.js a React component?

rottmann commented 9 years ago

Yes it is the react-router with a sample react component.

rottmann commented 9 years ago

I test it before on one port with a webpack dev only server, and it worked, then splitted it into 2 servers and got the problem.

Or is it possible to redirect router paths to index.html with webpack-dev-server? e.g. for a direct call of http://localhost:8080/some/path/4711

sokra commented 9 years ago

The log looks pretty good... It does't look like a connection/configuration issue.

cc @gaearon

gaearon commented 9 years ago

Can you put up a reproducible example in a repo so I could examine it?

rottmann commented 9 years ago

Funny, i create an example with the code above, everything worked. Then i try my project again and it worked too.

Hope it was the update of chrome that solved the problem and it is no other strange problem.

rottmann commented 9 years ago

Reopened the problem.

I try my app at my office computer and got the problem.

At home i run Win7 Host with a VirtualBox Mint 17 and it worked without a problem in the box. In the office i use a native Mint 17 with the same chrome (synced with the same plugins)... the browser not update the view.

Did somebody have an idea how i can determine the problem?

gaearon commented 9 years ago

Any change you have duplicate react in node_modules? For example, some other library may have it in its own node_modules..

rottmann commented 9 years ago
  "dependencies": {
    "react": "^0.12.2",
    "react-router": "^0.11.6"
  },
  "devDependencies": {
    "express": "^4.11.1",
    "jsx-loader": "^0.12.2",
    "react-hot-loader": "^1.1.3",
    "webpack": "^1.5.3",
    "webpack-dev-server": "^1.7.0"
  }

cleaned node_modules dir and install all modules new.

gaearon commented 9 years ago

Does https://github.com/gaearon/react-hot-boilerplate work on that computer?

rottmann commented 9 years ago

Thanks, found the problem: apparmor blocked something on port 8080. (previously i run a docker project and uninstalled docker)

Funny problem, script compilation run and browser message for reload appears, only the browser view not update thats why it was not obvious what was not working.

gaearon commented 9 years ago

Good, thank you for sharing!

bobzhang commented 9 years ago

I see the same problem @rottmann , how can I fix it? Logs below look perfectly fine, just chrome not updated ..

[WDS] App hot update...
bundle.js:8022 [WDS] App hot update...
bundle.js:7911 [HMR] Checking for updates on the server...
bundle.js:7945 [HMR] Updated modules:
bundle.js:7947 [HMR]  - 59
bundle.js:7897 [HMR] App is up to date.
mikealexander commented 8 years ago

@bobzhang did you find a solution? I am seeing the same problem.

mharmuth commented 8 years ago

I ran into the same problem yesterday. Any information on how to fix that?

@mikealexander Did you find a solution for your problem?

lxibarra commented 8 years ago

I have this same problem, anyone help

YamiOdymel commented 8 years ago

Same problem, the browser received the hot-update.js but updated nothing,

I tried to execute the code which is in the hot-update.js and still nothing happened.

YamiOdymel commented 8 years ago

Okay, I solved this problem by STOP USING webpack-merge for my config.entry.client.

module.exports = merge(config, 
{
    entry:
    {
        cilent: 
        [
            'webpack-hot-middleware/client', 
            config.entry.client
        ]
    }
})

CHANGE TO

config.entry.client = ['webpack-hot-middleware/client', config.entry.client]
wunderg commented 7 years ago

Did anyone find a solution?

Armour commented 7 years ago

I also met this problem, and I solved it by change

import { AppContainer } from 'react-hot-loader';
import App from 'js/App';

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

to

import { AppContainer } from 'react-hot-loader';
import App from 'js/App';

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

Seems the App won't refresh it self, so we need to re-import it, also need to make sure import it after(or say inside) the module.hot.accept function call.

Hope it helps :)

davincho commented 7 years ago

Thanks @Armour. I followed https://webpack.js.org/guides/hmr-react/ which says "Note that because webpack 2 has built-in support for ES2015 modules, you won't need to re-require your root component in module.hot.accept" but the browser did not update the view. After "re-requiring" the component within the module.hot.accept callback everything worked as expected.

chbinghu commented 7 years ago

Thanks @Armour, it works well! My index.js is:

import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'

import App from './containers/App'

const render = Component => { 
    ReactDOM.render(
        <AppContainer>
            <Component/>
        </AppContainer>,
        document.getElementById('react-root')
    )
}

render(App)

/** It doesn't work
 * 
    if(module.hot) {
        module.hot.accept('./containers/App', () => {
            render(App)
        })
    }
 */

// It works well
if(module.hot) {
    module.hot.accept('./containers/App', () => {
        const NextApp = require('./containers/App').default
        render(NextApp)
    })
}
tiodot commented 7 years ago

I have the same problem, there is interesting solution which use "self-accepting", the index.js is:

import React from 'react'
import ReactDOM from 'react-dom'
import { AppContainer } from 'react-hot-loader'

import App from './containers/App'

const render = Component => { 
    ReactDOM.render(
        <AppContainer>
            <Component/>
        </AppContainer>,
        document.getElementById('react-root')
    )
}

render(App)

/** It doesn't work
 * 
    if(module.hot) {
        module.hot.accept('./containers/App', () => {
            render(App)
        })
    }
 */

// It works well
if(module.hot) {
    module.hot.accept();
}
daviddelusenet commented 7 years ago

I'm also experiencing this issue. I've asked a question on StackOverflow which can be seen here:

http://stackoverflow.com/questions/43491310/cant-get-webpack-2-hmr-react-to-work

This works for me:

if(module.hot) {
  module.hot.accept();
}

This also works for me:

if(module.hot) {
  module.hot.accept('./components/TodoApp', () => {
    const NextApp = require('./components/TodoApp').default;
    render(NextApp)
  })
}

This doesn't:

if (module.hot) {
  module.hot.accept('./components/TodoApp', () => {
    render(TodoApp)
  });
}

Why doesn't the default setup work? Really confused.

hrasoa commented 7 years ago

Had the same issue since this morning and this fixed it !

ssynix commented 7 years ago

I had the same issue and it boils down to how module code generation works in your setup (through Babel/TypeScript). From my limited understanding, webpack 2 should be able to inject the require call using ES6 modules. I use TypeScript, and the below tsconfig values worked for me:

{
    "target": "es6",
    // "module":  Don't override this to anything
    "moduleResolution": "node",
}

I remember reading about a module: false parameter in Babel that did something similar for webpack + Babel configs.

module.hot.accept() and module.hot.accept('module') may not always be interchangeable. Make sure you understand what they each do.

derekdon commented 7 years ago

@ssynix I'm also using typescript (2.2.2) and I can't seem to get react-hot-loader@next it to work following the https://github.com/gaearon/react-hot-loader/tree/master/docs#webpack-2 example as @davincho did. Re-requiring the app in the hot reload handler works, as does just calling module.hot.accept(), but I'd like to get it working as documented. As I think @ssynix is right "it boils down to how module code generation works in your setup", I've been trying a number of different settings without much luck (would like to take advantage of tree shaking).

// main.tsx
import { AppContainer as HotContainer } from 'react-hot-loader';
import { default as App } from './containers/AppContainer';
...
const render = (Component: any) => {
    ReactDOM.render(
        <HotContainer>
            <Component
                store={store}
                routes={routes}
            />
        </HotContainer>,
        root
    );
};
...
if (module.hot) {
        module.hot.accept('./containers/AppContainer', () => {
            if (root) {
                ReactDOM.unmountComponentAtNode(root);
            }
            render(App);
        });
    }
//.babelrc
{
    "presets": [
        [
            "es2015",
            {
                "modules": false
            }
        ],
        "stage-0",
        "react"
    ],
    "plugins": [
        "react-hot-loader/babel",
        ...
    ]
}
// tsconfig.json
{
  "compilerOptions": {
    "isolatedModules": true,
    "moduleResolution": "node",
    "target": "es2015"
    ...
  }
}
wufenfen commented 7 years ago
if(module.hot) {
  module.hot.accept();
} 

This works for me too, in the angular app. Thanks so much~~~

jasondonnette commented 7 years ago

I struggled with this for a bit with Webpack 3 and got it working with the import() syntax. Not sure if this is helpful to anyone but here's what's working for me:

import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';

const run = async () => {
  const { Root } = await import('index');
  ReactDOM.render(
    <AppContainer>
      <Root />
    </AppContainer>,
    document.getElementById('react'),
  );
};

const loadedStates = ['complete', 'loaded', 'interactive'];
if (loadedStates.includes(document.readyState) && document.body) {
  run();
} else {
  window.addEventListener('DOMContentLoaded', run, false);
}

module.hot.accept('index', run);
gdepina commented 6 years ago

@swxy This works for me thanks a lot! I am using, webpack 2.2 + RRV4 + React-hot-loader v3 and webpack-hot-middleware.

if(module.hot) {
    module.hot.accept();
}
hedgepigdaniel commented 6 years ago

I had this issue and for me the underlying problem was misconfigured babel presets. Before (leading to exactly the same HMR situation as @daviddelusenet):

presets: [
  [ 'es2015', { modules: false } ],
  'stage-0',
   'react',
]

After (The usual approach now works):

presets: [
  [ 'env', { modules: false } ],
   'react',
]

I think options to a preset can be overriden by other presets, in this case presumably stage-0 also turned the modules transform back on.

Source: https://stackoverflow.com/questions/43491310/cant-get-webpack-2-hmr-react-to-work/43500626

jyotendra commented 6 years ago

Thanks @hedgepigdaniel. Its mentioned in the react-hot-loader doc here, that

"To make this work, you'll need to opt out of Babel transpiling ES2015 modules by changing the Babel ES2015 preset to be ["es2015", { "modules": false }]"

gpnoel commented 6 years ago

I finally got this working after 5+ hours looking through many different github issues and tutorials.

First off, versions for what I'm using currently:

Two things helped resolve this issue and have the browser reload on source code change:

  1. setting webpack's config for hot to false -- hot: false

People said that they did not include this property in their config s but I had to set the value to false for it to work for me

  1. including a bundle to the client for webpack-dev-server:
entry: {
  hmr_endpoint: 'webpack-dev-server/client?http://localhost:8008'
}

Whatever you name it doesn't matter. including it in an array works as well; just be wary of mismatching types if you are doing this all through Node's api like I am.

entry: [
  'webpack-dev-server/client?http://localhost:8008'
]

Obviously change the port to whichever one you are working with in your project. Hope this helps someone and resources I looked at that helped me come to this: 1 2 3

ejoo commented 5 years ago

@tiodot 's solution worked with Typescript + React but failed with Redux and Redux Saga Integrated.