Hot Module Replacement setup
For projects created before version 0.49.0, use this guide.
The instructions below assume you're running RN 0.49.0 or newer.
Jump to: Automatic setup | Manual setup
For smaller projects, we provide automatic option for setting up Hot Module Replacement. If your project meets the following criteria, you can use our one-line setup for HMR:
- Single root component exported from
App.js
file withdefault
keyword (export default
) - Single
AppRegistry.registerComponent
call inindex.js
So, index.js
file should look more or less like this:
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('MyProject', () => App);
All default index.js
and App.js
files created by react-native init
work with the automatic setup out of the box.
Automatic setup
In order to enable HMR add import 'haul/hot';
at the top of the index.js
file. Then, from within Developer menu tap Enable Hot Reloading.
Manual setup
Navigate to: API docs
Jump to: Examples
For advanced projects or the ones with different structure, we provide our own HMR API, which allows you to enable HMR regardless of which navigation library you use and how you structure your code.
This guide below is not a list of steps, but rather a set of rules and tips on how to setup HMR, since we don't know how your project looks like. However, if you're using one of the following navigation libraries, please refer to associated guide:
Haul uses react-hot-loader
to support HMR, so the first step is to patch React's createElement
and createFactory
functions to use react-proxy
. Proxied component can be updated with a new implementation but it's state is persisted between updates.
In order to do that, add the following line to __the root file__
- the one that is used as an entrypoint, before any other code:
import 'haul/hot/patch';
It's important that the code from haul/hot/patch
is evaluated before anything else!
Now, we need to wrap the root component factories using makeHot
function. Root component factory is a function that returns a root component:
() => RootComponent
You can encounter those in AppRegistry.registerComponent
or Navigation.registerComponent
calls:
AppRegistry.registerComponent('MyApp', () => MyApp);
// or
Navigation.registerComponent('MyScreen', () => MyScreen);
After wrapping those with makeHot
it should look like this:
AppRegistry.registerComponent('MyApp', makeHot(() => MyApp));
// or
Navigation.registerComponent('MyScreen', makeHot(() => MyScreen));
If your app has multiple root components (usually they're treated as screens), you need to pass the second argument to makeHot
call, which is a id
of the component factory. It will be used later to tell which component needs to be redrawn:
Navigation.registerComponent('MyScreen1', makeHot(() => MyScreen1, 'MyScreen1'));
Navigation.registerComponent('MyScreen2', makeHot(() => MyScreen2, 'MyScreen2'));
Navigation.registerComponent('MyScreen3', makeHot(() => MyScreen3, 'MyScreen3'));
All of the functions we use in this guide can be imported from haul/hot
:
import {
makeHot,
tryUpdateSelf,
callOnce,
clearCacheFor,
redraw
} from 'haul/hot';
The last thing is to acually tell the Webpack which modules we want to accept and how to update them. You can safely copy-pase the following snippet:
if (module.hot) {
module.hot.accept('', () => {
clearCacheFor(require.resolve(''));
redraw(() => require('').default);
});
}
Now, you need to replace empty strings with the paths according to the following rules:
- If you have a single root component, it's better to extract the
module.hot.accept
logic to a parent file (the one that imports the root component), then you can replace all of the empty strings with path to that component:// file: index.js import App from './App'; /* ... */ if (module.hot) { module.hot.accept(() => {}) module.hot.accept('./App', () => { clearCacheFor(require.resolve('./App')); redraw(() => require('./App').default); }); }
- If you have a multi root component, all of the paths should point to the root component and each of the root components should have a separate
module.hot.accept
call:// file: index.js import Screen1 from './Screen1'; import Screen2 from './Screen2'; /* ... */ if (module.hot) { module.hot.accept(() => {}) module.hot.accept('./Screen1', () => { clearCacheFor(require.resolve('./Screen1')); redraw(() => require('./Screen1').default, 'Screen1'); }); module.hot.accept('./Screen2', () => { clearCacheFor(require.resolve('./Screen2')); redraw(() => require('./Screen2').default, 'Screen2'); }); }
Examples
Single root component in a entry file
import 'haul/hot/patch';
import {
makeHot,
tryUpdateSelf,
callOnce,
clearCacheFor,
redraw
} from 'haul/hot';
import {
AppRegistry,
} from 'react-native';
import App from './App';
AppRegistry.registerComponent('MyApp', makeHot(() => App));
if (module.hot) {
module.hot.accept(() => {})
module.hot.accept(['./App'], () => {
clearCacheFor(require.resolve('./App'));
redraw(() => require('./App').default);
});
}
Multi root component
// index.js
import 'haul/hot/patch';
import './screens';
// screens.js
import {
makeHot,
tryUpdateSelf,
callOnce,
clearCacheFor,
redraw
} from 'haul/hot';
import Navigation from 'some-naviagtion-lib';
import Calendar from './Calendar';
import Location from './Location';
Navigation.registerComponent('Calendar', makeHot(() => Calendar, 'Calendar'));
Navigation.registerComponent('Location', makeHot(() => Location, 'Location'));
if (module.hot) {
module.hot.accept('./Calendar', () => {
clearCacheFor(require.resolve('./Calendar'));
redraw(() => require('./Calendar').default, 'Calendar');
});
module.hot.accept('./Location', () => {
clearCacheFor(require.resolve('./Location'));
redraw(() => require('./Location').default, 'Location');
});
}