Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hot module replacement for RN 0.12 #103

Open
elliottsj opened this issue Oct 11, 2015 · 32 comments
Open

Fix hot module replacement for RN 0.12 #103

elliottsj opened this issue Oct 11, 2015 · 32 comments

Comments

@elliottsj
Copy link
Collaborator

New in react-native@0.12: when using the Chrome debugger, the app bundle is now run inside a web worker: facebook/react-native#1632, facebook/react-native@8db35d4

This means that an app bundle built with webpack's hot module replacement enabled will raise errors such as #99, and hot module replacement will fail.


I'm able to get HMR working on the rn-0.12-hmr branch by doing the following:

  • Use webpack-dev-middleware + webpack-hot-middleware instead of webpack/hot/only-dev-server + webpack-dev-server/client:

    webpack-dev-server depends on window.postMessage to broadcast "webpackHotUpdate" events to any loaded scripts (usually just 'webpack/hot/dev-server' or 'webpack/hot/only-dev-server'), which does not work in the context of a web worker since web workers have a different postMessage API. webpack-hot-middleware is an alternative which combines the functionality of both webpack/hot/only-dev-server and webpack-dev-server/client.

  • Patch webpack so it uses importScripts inside a web worker instead of the default behaviour of appending a <script> with the new hot-reloaded module: elliottsj/webpack@743a00a.

    Known issue: this inexplicable error is thrown every time a module is hot reloaded:
    Edit: turns out this was caused by adding webpack.HotModuleReplacementPlugin() to the config twice instead of once (-‸ლ)

    screenshot 2015-10-11 15 07 53

    Not sure why this happens; the original error message & stack gets lost somewhere.

Try out rn-0.12-hmr/Examples/BabelES6 for a working example.

My patch to webpack seems quite hacky; I think the proper solution is to make something like 'WebWorkerMainTemplate.runtime.js', but I don't know enough about webpack internals to know what to do here.

Does anyone have any ideas on the proper way to do HMR from inside the web worker? cc @gaearon @sokra

@sokra
Copy link

sokra commented Oct 13, 2015

WebWorkerMainTemplate.runtime.js is the way to go. Settings target: "webworker" will enabled it. Should be pretty easy to write as importScripts is sync.

WebWorkerHotUpdateChunkTemplatePlugin.js is also needed to defined the hot update chunk format for web workers, but it can be equal to JsonpHotUpdateChunkTemplatePlugin.js.

If someone want to do a PR to webpack, I would be happy...

@elliottsj
Copy link
Collaborator Author

Opened PR to support HMR in web workers: webpack/webpack#1521

@dapetcu21
Copy link

I tried using your branch, but I get this warning the first time I change a file (and HMR doesn't work):
screen shot 2015-10-20 at 12 50 51
...
screen shot 2015-10-20 at 12 51 12

Any opinions?

UPDATE: Nevermind. It worked with your webpack PR.

@paolorovella
Copy link

@dapetcu21 Hi, i'm stuck with this webworker error and maybe you can help me. Where did you put the "var worker = new Worker("bundle.js");"?

@elliottsj
Copy link
Collaborator Author

@paolorovella You don't need var worker = new Worker("bundle.js");. The RN debugger will load the bundle into a web worker for you; try out the rn-0.12-hmr/Examples/BabelES6 example.

@paolorovella
Copy link

I finally did it! I'm using RN 0.12 + react-native-webpack-server#rn-0.12-hmr + elliottsj/webpack#web-worker-hmr + webpack-hot-middleware without var worker = new Worker('bundle.js'); and without target:"webworker...thank you so much :)

@dapetcu21
Copy link

@paolorovella: Yup. Exactly same setup worked for me, as well. I was initially trying the rn-0.12-hack branch that was mentioned earlier.

@elliottsj
Copy link
Collaborator Author

Just pushed an update to rn-0.12-hmr/Examples/BabelES6: it now uses the changes from webpack/webpack#1521 instead of the hack.

@paolorovella
Copy link

Hi! Since i was very happy for the working HMR in my simulator, i've decided to try on my iPhone 6. Unfortunately it doesn't work and this is my console. Any ideas?
schermata 2015-10-22 alle 17 37 28

@dapetcu21
Copy link

You need to change localhost to your computer's IP/hostname in AppDelegate.m

@dapetcu21
Copy link

Also, webpack's publicPath should include your hostname as well

@paolorovella
Copy link

I've already done it but it doesn't work :/ if it can helps, if i go to http://localhost:8082/ i get "Cannot get /" but if i go to http://192.168.1.156:8082/ the page is not available.

@ptomasroos
Copy link
Contributor

Firewall settings?

On 22 Oct 2015, at 17:49, Paolo Rovella notifications@github.com wrote:

I've already done it but it doesn't work :/ if it can helps, if i go to http://localhost:8082/ i get "Cannot get /" but if i go to http://192.168.1.156:8082/ the page is not available.


Reply to this email directly or view it on GitHub.

@paolorovella
Copy link

Firewall deactivated :) could it be a port forwarding router problem? 8082 closed port i mean

@dapetcu21
Copy link

It could be that rnws binds only to localhost: app.listen('localhost', port)

@paolorovella
Copy link

@dapetcu21 i've changed in package.json -> HOT=1 ./node_modules/.bin/react-native-webpack-server start --hot --hostname 192.168.1.156 and now http://192.168.1.156:8082/index.ios.js gives me the bundle but when i update a component the HMR is not starting

@paolorovella
Copy link

@dapetcu21 i've forgotten to set http://192.168.1.156:8082 in 'webpack-hot-middleware/client?path=http://192.168.1.156:8082/__webpack_hmr&overlay=false' now everything is working :) thank you so much

@niftylettuce
Copy link

@niftylettuce
Copy link

@elliottsj Hot reloading still doesn't seem to work - and now neither does LiveReload

@niftylettuce
Copy link

@elliottsj Here's some output if it helps... this occurs after I save a file (the debugger/websocket stuff is connected already, it looks like the hotpack files might not be reloading properly or triggering intent refresh or something, not sure...?)

webpack built 872a39c5ec3c5bdae6a4 in 441ms
Hash: 872a39c5ec3c5bdae6a4
Version: webpack 1.12.2
Time: 441ms
                                   Asset      Size  Chunks             Chunk Names
                            index.ios.js    117 kB       0  [emitted]  index.ios
                        index.android.js    104 kB       1  [emitted]  index.android
    0.dfd585fc6ce8bf86eb49.hot-update.js   3.79 kB       0  [emitted]  index.ios
    dfd585fc6ce8bf86eb49.hot-update.json  36 bytes          [emitted]
                        index.ios.js.map    160 kB       0  [emitted]  index.ios
0.dfd585fc6ce8bf86eb49.hot-update.js.map    4.2 kB       0  [emitted]  index.ios
                    index.android.js.map    140 kB       1  [emitted]  index.android
chunk    {0} index.ios.js, 0.dfd585fc6ce8bf86eb49.hot-update.js, index.ios.js.map, 0.dfd585fc6ce8bf86eb49.hot-update.js.map (index.ios) 86.1 kB [rendered]
chunk    {1} index.android.js, index.android.js.map (index.android) 73.9 kB [rendered]
webpack: bundle is now VALID.
[3:29:27 PM] <START> find dependencies
[3:29:27 PM] <END>   find dependencies (96ms)
[3:29:27 PM] <START> transform
transforming [========================================] 100% 314/314
[3:29:27 PM] <END>   transform (114ms)

@niftylettuce
Copy link

Invalid HMR message: {"action":"built","time":441,"hash":"872a39c5ec3c5bdae6a4","warnings":[],"errors":[],"modules":{"0":"multi index.android","1":".//react-native/Libraries/react-native/react-native.js","2":".//react-transform-hmr//react-proxy//lodash/lang/isObject.js","3":".//react-transform-hmr//react-proxy//lodash/internal/isArrayLike.js","4":".//react-transform-hmr//react-proxy//lodash/internal/isObjectLike.js","5":".//react-transform-hmr//react-proxy//lodash/internal/getNative.js","6":".//react-transform-hmr//react-proxy//lodash/internal/isLength.js","7":"(webpack)/buildin/module.js","8":".//babel-runtime/helpers/interop-require-default.js","9":".//react-transform-hmr/lib/index.js","10":".//react-transform-hmr//react-proxy//lodash/internal/isIndex.js","11":".//react-transform-hmr//react-proxy//lodash/lang/isArguments.js","12":".//react-transform-hmr//react-proxy//lodash/lang/isArray.js","13":".//babel-runtime//core-js/library/modules/$.core.js","14":".//react-transform-hmr//react-proxy//lodash/function/restParam.js","15":".//react-transform-hmr//react-proxy//lodash/object/keys.js","16":"external "NativeModules"","17":".//react-native-button/Button.js","18":".//react-native-button/coalesceNonElementChildren.js","19":".//react-native-form/index.js","20":".//babel-runtime/core-js/object/assign.js","21":".//babel-runtime/helpers/extends.js","22":".//babel-runtime//core-js/library/fn/object/assign.js","23":".//babel-runtime//core-js/library/modules/$.assign.js","24":".//babel-runtime//core-js/library/modules/$.cof.js","25":".//babel-runtime//core-js/library/modules/$.def.js","26":".//babel-runtime//core-js/library/modules/$.defined.js","27":".//babel-runtime//core-js/library/modules/$.fails.js","28":".//babel-runtime//core-js/library/modules/$.global.js","29":".//babel-runtime//core-js/library/modules/$.iobject.js","30":".//babel-runtime//core-js/library/modules/$.js","31":".//babel-runtime//core-js/library/modules/$.to-object.js","32":".//babel-runtime//core-js/library/modules/es6.object.assign.js","33":".//react-transform-hmr//global/window.js","34":".//react-transform-hmr//react-proxy/modules/bindAutoBindMethods.js","35":".//react-transform-hmr//react-proxy/modules/createClassProxy.js","36":".//react-transform-hmr//react-proxy/modules/createPrototypeProxy.js","37":".//react-transform-hmr//react-proxy/modules/deleteUnknownAutoBindMethods.js","38":".//react-transform-hmr//react-proxy/modules/index.js","39":".//react-transform-hmr//react-proxy//lodash/array/difference.js","40":".//react-transform-hmr//react-proxy//lodash/internal/SetCache.js","41":".//react-transform-hmr//react-proxy//lodash/internal/arrayPush.js","42":".//react-transform-hmr//react-proxy//lodash/internal/assignWith.js","43":".//react-transform-hmr//react-proxy//lodash/internal/baseAssign.js","44":".//react-transform-hmr//react-proxy//lodash/internal/baseCopy.js","45":".//react-transform-hmr//react-proxy//lodash/internal/baseDifference.js","46":".//react-transform-hmr//react-proxy//lodash/internal/baseFlatten.js","47":".//react-transform-hmr//react-proxy//lodash/internal/baseIndexOf.js","48":".//react-transform-hmr//react-proxy//lodash/internal/baseProperty.js","49":".//react-transform-hmr//react-proxy//lodash/internal/bindCallback.js","50":".//react-transform-hmr//react-proxy//lodash/internal/cacheIndexOf.js","51":".//react-transform-hmr//react-proxy//lodash/internal/cachePush.js","52":".//react-transform-hmr//react-proxy//lodash/internal/createAssigner.js","53":".//react-transform-hmr//react-proxy//lodash/internal/createCache.js","54":".//react-transform-hmr//react-proxy//lodash/internal/getLength.js","55":".//react-transform-hmr//react-proxy//lodash/internal/indexOfNaN.js","56":".//react-transform-hmr//react-proxy//lodash/internal/isIterateeCall.js","57":".//react-transform-hmr//react-proxy//lodash/internal/shimKeys.js","58":".//react-transform-hmr//react-proxy//lodash/lang/isFunction.js","59":".//react-transform-hmr//react-proxy//lodash/lang/isNative.js","60":".//react-transform-hmr//react-proxy//lodash/object/assign.js","61":".//react-transform-hmr//react-proxy//lodash/object/keysIn.js","62":".//react-transform-hmr//react-proxy//lodash/utility/identity.js","63":".//react-transform-hmr//react-proxy//react-deep-force-update/lib/index.js","64":"external "ActionSheetIOS"","65":"external "ActivityIndicatorIOS"","66":"external "AdSupportIOS"","67":"external "AlertIOS"","68":"external "Animated"","69":"external "AppRegistry"","70":"external "AppStateIOS"","71":"external "AsyncStorage"","72":"external "BackAndroid"","73":"external "CameraRoll"","74":"external "DatePickerIOS"","75":"external "Dimensions"","76":"external "DrawerLayoutAndroid"","77":"external "Easing"","78":"external "EdgeInsetsPropType"","79":"external "Image"","80":"external "ImagePickerIOS"","81":"external "InteractionManager"","82":"external "LayoutAnimation"","83":"external "LinkedStateMixin"","84":"external "LinkingIOS"","85":"external "ListView"","86":"external "MapView"","87":"external "Modal"","88":"external "Navigator"","89":"external "NavigatorIOS"","90":"external "NetInfo"","91":"external "PanResponder"","92":"external "PickerIOS"","93":"external "PixelRatio"","94":"external "Platform"","95":"external "PointPropType"","96":"external "ProgressBarAndroid"","97":"external "ProgressViewIOS"","98":"external "PushNotificationIOS"","99":"external "RCTDeviceEventEmitter"","100":"external "RCTNativeAppEventEmitter"","101":"external "React"","102":"external "ReactComponentWithPureRenderMixin"","103":"external "ReactDefaultPerf"","104":"external "ReactFragment"","105":"external "ReactTestUtils"","106":"external "ReactUpdates"","107":"external "ScrollView"","108":"external "SegmentedControlIOS"","109":"external "Settings"","110":"external "SliderIOS"","111":"external "StatusBarIOS"","112":"external "StyleSheet"","113":"external "SwitchAndroid"","114":"external "SwitchIOS"","115":"external "TabBarIOS"","116":"external "Text"","117":"external "TextInput"","118":"external "ToastAndroid"","119":"external "ToolbarAndroid"","120":"external "TouchableHighlight"","121":"external "TouchableNativeFeedback"","122":"external "TouchableOpacity"","123":"external "TouchableWithoutFeedback"","124":"external "VibrationIOS"","125":"external "View"","126":"external "WebView"","127":"external "cloneWithProps"","128":"external "processColor"","129":"external "requireNativeComponent"","130":"external "update"","131":"./src/main.android.js","132":"./src/main.ios.js","133":".//react-native-webpack-server/hot/entry.js","134":"(webpack)-hot-middleware/client-overlay.js","135":"(webpack)-hot-middleware/client.js?path=http://localhost:8082/__webpack_hmr&overlay=false","136":"(webpack)-hot-middleware/~/querystring/decode.js","137":"(webpack)-hot-middleware/~/querystring/encode.js","138":"(webpack)-hot-middleware/~/querystring/index.js","139":"(webpack)-hot-middleware/~/strip-ansi/index.js","140":"(webpack)-hot-middleware/~/strip-ansi/~/ansi-regex/index.js","141":"(webpack)-hot-middleware/process-update.js"}}
ReferenceError: hotDownloadManifest is not defined

@niftylettuce
Copy link

screen shot 2015-10-26 at 4 38 26 pm

@niftylettuce
Copy link

Here's my webpack.config.js:

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

var config = {
  context: __dirname,
  debug: true,
  devtool: 'source-map',
  watch: true,
  entry: {
    'index.ios': ['./src/main.ios.js']
    'index.android': ['./src/main.android.js'],
  },
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].js',
  },
  module: {
    loaders: [
      {
        test: /\.(js|jsx|es6)$/,
        loader: 'babel-loader',
        include: [
          path.resolve(__dirname, 'src'),
          path.resolve(__dirname, 'node_modules/react-native-button'),
          path.resolve(__dirname, 'node_modules/react-native-form')
        ],
        query: {
          optional: [
            'runtime'
          ],
          stage: 0,
          plugins: []
        }
      }
    ]
  },
  resolve: { extensions: ['', '.js', '.jsx', '.es6'] },
  plugins: []
};

// Hot loader
if (process.env.HOT) {
  config.devtool = 'source-map';
  config.entry['index.ios'].unshift(
    'react-native-webpack-server/hot/entry',
    'webpack-hot-middleware/client?path=http://localhost:8082/__webpack_hmr&overlay=false'
  );
  config.output.publicPath = 'http://localhost:8082/';
  config.plugins.unshift(
    new webpack.optimize.OccurrenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  );
  config.module.loaders[0].query.plugins.push('react-transform');
  config.module.loaders[0].query.extra = {
    'react-transform': {
      transforms: [
        {
          transform: 'react-transform-hmr',
          imports: ['react-native'],
          locals: ['module']
        }
      ]
    }
  };
}

// Production config
if (process.env.NODE_ENV === 'production') {
  config.plugins.push(new webpack.optimize.OccurrenceOrderPlugin());
  config.plugins.push(new webpack.optimize.UglifyJsPlugin());
}

module.exports = config;

Here's my package.json:

{
  "name": "App",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "bundle": "react-native-webpack-server bundle",
    "start": "react-native-webpack-server start",
    "hot": "HOT=1 react-native-webpack-server start --hot",
    "start-android-webpack-server": "HOT=1 react-native-webpack-server start --hot -e index.android -P 9090 -p 9091 -w 9092",
    "install-app-to-android-device": "cd android && ./gradlew installDebug",
    "setup-reverse-tcp-for-android-device": "adb reverse tcp:8081 tcp:9090",
    "launch-android-app-on-device": "cd android && adb shell am start -n com.\"$npm_package_name\"/.MainActivity",
    "android": "npm run-script setup-reverse-tcp-for-android-device && npm run-script install-app-to-android-device && npm run-script launch-android-app-on-device && echo \"Please Reload JS on the app from the menu after the webpack server starts below\" && npm run-script start-android-webpack-server"
  },
  "dependencies": {
    "react-native": "~0.12.0",
    "react-native-button": "^1.2.1",
    "react-native-form": "^0.1.6"
  },
  "devDependencies": {
    "babel-core": "^5.8.25",
    "babel-loader": "^5.3.2",
    "babel-plugin-react-transform": "^1.1.1",
    "babel-runtime": "^5.8.25",
    "eslint-plugin-react": "^3.6.1",
    "react-native-webpack-server": "mjohnston/react-native-webpack-server#rn-0.12-hmr",
    "react-transform-hmr": "^1.0.1",
    "webpack": "elliottsj/webpack#web-worker-hmr"
  }
}

@elliottsj
Copy link
Collaborator Author

@niftylettuce You also need target: 'webworker' in your webpack config, e.g. https://github.com/mjohnston/react-native-webpack-server/blob/rn-0.12-hmr/Examples/BabelES6/webpack.config.js#L7

I'm happy to help on Discord#react-native-webpack if you need more help 😄

@niftylettuce
Copy link

That didn't work, I got hotModule not defined error above if I put
webworker:true in config
On Oct 26, 2015 4:53 PM, "Spencer Elliott" notifications@github.com wrote:

@niftylettuce https://github.com/niftylettuce You also need target:
'webworker' in your webpack config, e.g.
https://github.com/mjohnston/react-native-webpack-server/blob/rn-0.12-hmr/Examples/BabelES6/webpack.config.js#L7

I'm happy to help on Discord#react-native-webpack
http://www.reactiflux.com/ if you need more help [image: 😄]


Reply to this email directly or view it on GitHub
#103 (comment)
.

@niftylettuce
Copy link

Got it working! 🎈

@ptomasroos
Copy link
Contributor

Then share it! Step by step :-)

Sent from my iPhone

On 27 Oct 2015, at 01:09, niftylettuce notifications@github.com wrote:

Got it working!


Reply to this email directly or view it on GitHub.

@quangrau
Copy link

quangrau commented Nov 3, 2015

@niftylettuce Did you make it worked? I have the same issue with you:
Invalid HMR message: ....
I also add target: 'webworker' to config file but nothing happen.

Please share your tips.

@RyGuyM
Copy link

RyGuyM commented Nov 4, 2015

@niftylettuce I also have the same issue as you. How did you make it work? Hope you can help us, thanks!

@niftylettuce
Copy link

I don't recommend to use hotloading until it has complete Android and iOS
support. The headache and time ensued for me getting it to work, but now
having to turn it off is not worth having it. Just CMD+R and reload.
You don't need experimental hot reloading.
On Nov 4, 2015 12:55 PM, "RyGuyM" notifications@github.com wrote:

@niftylettuce https://github.com/niftylettuce I also have the same
issue as you. How did you make it work? Hope you can help us, thanks!


Reply to this email directly or view it on GitHub
#103 (comment)
.

@ghost
Copy link

ghost commented Apr 8, 2016

Thanks for publishing 0.9.0! Given recent changes does the title of this still make sense? I believe this may be the root cause of https://github.com/jhabdas/react-native-webpack-starter-kit/issues/73

@philikon
Copy link
Collaborator

philikon commented Apr 8, 2016

Not sure. I'm not really interested in hot module replacement. If somebody wants to fix it for RN 0.13+, I'd be happy to entertain PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants