Skip to content

Commit

Permalink
Use lazy getters to reduce require('react-native') overhead
Browse files Browse the repository at this point in the history
Summary:
public

In the open source React Native implementation, the recommended approach for importing modules is by importing a the `ReactNative` object, which includes all available modules.

This is rather inefficient because it ends up initializing all the JS modules, even if you don't use them.

This diff switches the properties of the `ReactNative ` object to lazy getter functions, which defers the `require` until the module is actually requested.

This doesn't prevent unused modules from being included in the JS bundle, but it will prevent them from being initialized unless/until they are used.

Reviewed By: vjeux

Differential Revision: D2722993

fb-gh-sync-id: 0e9a2beb3aa6cd087a0592bd59a8f9242040be0c
  • Loading branch information
nicklockwood authored and facebook-github-bot-5 committed Dec 8, 2015
1 parent 0f98ded commit f9b744d
Showing 1 changed file with 89 additions and 85 deletions.
174 changes: 89 additions & 85 deletions Libraries/react-native/react-native.js
Expand Up @@ -6,110 +6,114 @@
* LICENSE file in the root directory of this source tree. An additional grant * LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @flow * @noflow - get/set properties not yet supported by flow. also `...require(x)` is broken #6560135
*/ */
'use strict'; 'use strict';


// Export React, plus some native additions. // Export React, plus some native additions.
// var ReactNative = {
// The use of Object.create/assign is to work around a Flow bug (#6560135).
// Once that is fixed, change this back to
//
// var ReactNative = {...require('React'), /* additions */}
//
var ReactNative = Object.assign(Object.create(require('React')), {
// Components // Components
ActivityIndicatorIOS: require('ActivityIndicatorIOS'), get ActivityIndicatorIOS() { return require('ActivityIndicatorIOS'); },
ART: require('ReactNativeART'), get ART() { return require('ReactNativeART'); },
DatePickerIOS: require('DatePickerIOS'), get DatePickerIOS() { return require('DatePickerIOS'); },
DrawerLayoutAndroid: require('DrawerLayoutAndroid'), get DrawerLayoutAndroid() { return require('DrawerLayoutAndroid'); },
Image: require('Image'), get Image() { return require('Image'); },
ListView: require('ListView'), get ListView() { return require('ListView'); },
MapView: require('MapView'), get MapView() { return require('MapView'); },
Modal: require('Modal'), get Modal() { return require('Modal'); },
Navigator: require('Navigator'), get Navigator() { return require('Navigator'); },
NavigatorIOS: require('NavigatorIOS'), get NavigatorIOS() { return require('NavigatorIOS'); },
PickerIOS: require('PickerIOS'), get PickerIOS() { return require('PickerIOS'); },
ProgressBarAndroid: require('ProgressBarAndroid'), get ProgressBarAndroid() { return require('ProgressBarAndroid'); },
ProgressViewIOS: require('ProgressViewIOS'), get ProgressViewIOS() { return require('ProgressViewIOS'); },
ScrollView: require('ScrollView'), get ScrollView() { return require('ScrollView'); },
SegmentedControlIOS: require('SegmentedControlIOS'), get SegmentedControlIOS() { return require('SegmentedControlIOS'); },
SliderIOS: require('SliderIOS'), get SliderIOS() { return require('SliderIOS'); },
SnapshotViewIOS: require('SnapshotViewIOS'), get SnapshotViewIOS() { return require('SnapshotViewIOS'); },
Switch: require('Switch'), get Switch() { return require('Switch'); },
PullToRefreshViewAndroid: require('PullToRefreshViewAndroid'), get PullToRefreshViewAndroid() { return require('PullToRefreshViewAndroid'); },
SwitchAndroid: require('SwitchAndroid'), get SwitchAndroid() { return require('SwitchAndroid'); },
SwitchIOS: require('SwitchIOS'), get SwitchIOS() { return require('SwitchIOS'); },
TabBarIOS: require('TabBarIOS'), get TabBarIOS() { return require('TabBarIOS'); },
Text: require('Text'), get Text() { return require('Text'); },
TextInput: require('TextInput'), get TextInput() { return require('TextInput'); },
ToastAndroid: require('ToastAndroid'), get ToastAndroid() { return require('ToastAndroid'); },
ToolbarAndroid: require('ToolbarAndroid'), get ToolbarAndroid() { return require('ToolbarAndroid'); },
Touchable: require('Touchable'), get Touchable() { return require('Touchable'); },
TouchableHighlight: require('TouchableHighlight'), get TouchableHighlight() { return require('TouchableHighlight'); },
TouchableNativeFeedback: require('TouchableNativeFeedback'), get TouchableNativeFeedback() { return require('TouchableNativeFeedback'); },
TouchableOpacity: require('TouchableOpacity'), get TouchableOpacity() { return require('TouchableOpacity'); },
TouchableWithoutFeedback: require('TouchableWithoutFeedback'), get TouchableWithoutFeedback() { return require('TouchableWithoutFeedback'); },
View: require('View'), get View() { return require('View'); },
ViewPagerAndroid: require('ViewPagerAndroid'), get ViewPagerAndroid() { return require('ViewPagerAndroid'); },
WebView: require('WebView'), get WebView() { return require('WebView'); },


// APIs // APIs
ActionSheetIOS: require('ActionSheetIOS'), get ActionSheetIOS() { return require('ActionSheetIOS'); },
AdSupportIOS: require('AdSupportIOS'), get AdSupportIOS() { return require('AdSupportIOS'); },
AlertIOS: require('AlertIOS'), get AlertIOS() { return require('AlertIOS'); },
Animated: require('Animated'), get Animated() { return require('Animated'); },
AppRegistry: require('AppRegistry'), get AppRegistry() { return require('AppRegistry'); },
AppStateIOS: require('AppStateIOS'), get AppStateIOS() { return require('AppStateIOS'); },
AsyncStorage: require('AsyncStorage'), get AsyncStorage() { return require('AsyncStorage'); },
BackAndroid: require('BackAndroid'), get BackAndroid() { return require('BackAndroid'); },
CameraRoll: require('CameraRoll'), get CameraRoll() { return require('CameraRoll'); },
Dimensions: require('Dimensions'), get Dimensions() { return require('Dimensions'); },
Easing: require('Easing'), get Easing() { return require('Easing'); },
ImagePickerIOS: require('ImagePickerIOS'), get ImagePickerIOS() { return require('ImagePickerIOS'); },
IntentAndroid: require('IntentAndroid'), get IntentAndroid() { return require('IntentAndroid'); },
InteractionManager: require('InteractionManager'), get InteractionManager() { return require('InteractionManager'); },
LayoutAnimation: require('LayoutAnimation'), get LayoutAnimation() { return require('LayoutAnimation'); },
LinkingIOS: require('LinkingIOS'), get LinkingIOS() { return require('LinkingIOS'); },
NetInfo: require('NetInfo'), get NetInfo() { return require('NetInfo'); },
PanResponder: require('PanResponder'), get PanResponder() { return require('PanResponder'); },
PixelRatio: require('PixelRatio'), get PixelRatio() { return require('PixelRatio'); },
PushNotificationIOS: require('PushNotificationIOS'), get PushNotificationIOS() { return require('PushNotificationIOS'); },
Settings: require('Settings'), get Settings() { return require('Settings'); },
StatusBarIOS: require('StatusBarIOS'), get StatusBarIOS() { return require('StatusBarIOS'); },
StyleSheet: require('StyleSheet'), get StyleSheet() { return require('StyleSheet'); },
UIManager: require('UIManager'), get UIManager() { return require('UIManager'); },
VibrationIOS: require('VibrationIOS'), get VibrationIOS() { return require('VibrationIOS'); },


// Plugins // Plugins
DeviceEventEmitter: require('RCTDeviceEventEmitter'), get DeviceEventEmitter() { return require('RCTDeviceEventEmitter'); },
NativeAppEventEmitter: require('RCTNativeAppEventEmitter'), get NativeAppEventEmitter() { return require('RCTNativeAppEventEmitter'); },
NativeModules: require('NativeModules'), get NativeModules() { return require('NativeModules'); },
Platform: require('Platform'), get Platform() { return require('Platform'); },
processColor: require('processColor'), get processColor() { return require('processColor'); },
requireNativeComponent: require('requireNativeComponent'), get requireNativeComponent() { return require('requireNativeComponent'); },


// Prop Types // Prop Types
EdgeInsetsPropType: require('EdgeInsetsPropType'), get EdgeInsetsPropType() { return require('EdgeInsetsPropType'); },
PointPropType: require('PointPropType'), get PointPropType() { return require('PointPropType'); },


// See http://facebook.github.io/react/docs/addons.html // See http://facebook.github.io/react/docs/addons.html
addons: { addons: {
LinkedStateMixin: require('LinkedStateMixin'), get LinkedStateMixin() { return require('LinkedStateMixin'); },
Perf: undefined, Perf: undefined,
PureRenderMixin: require('ReactComponentWithPureRenderMixin'), get PureRenderMixin() { return require('ReactComponentWithPureRenderMixin'); },
TestModule: require('NativeModules').TestModule, get TestModule() { return require('NativeModules').TestModule; },
TestUtils: undefined, TestUtils: undefined,
batchedUpdates: require('ReactUpdates').batchedUpdates, get batchedUpdates() { return require('ReactUpdates').batchedUpdates; },
cloneWithProps: require('cloneWithProps'), get cloneWithProps() { return require('cloneWithProps'); },
createFragment: require('ReactFragment').create, get createFragment() { return require('ReactFragment').create; },
update: require('update'), get update() { return require('update'); },
}, },
});
// Note: this must be placed last to prevent eager
// evaluation of the getter-wrapped submodules above
...require('React'),

This comment has been minimized.

Copy link
@esamattis

This comment has been minimized.

Copy link
@atticoos

atticoos Feb 4, 2016

Contributor

Having the same problem.

This comment has been minimized.

Copy link
@yoniholmes

yoniholmes Mar 21, 2016

I'm running into the same problems with tape. Babel in node doesn't want to parse any file inside of /node_modules, apparently the assumption being that those files should already have been transpiled into es5. At time of writing, I haven't been able to force babel to parse those files – has anyone else had any success here?

This comment has been minimized.

Copy link
@jbrodriguez

jbrodriguez Apr 28, 2016

There are some changes coming in 0.25 that might help with this issue since ...require('React'), is being removed https://github.com/facebook/react-native/blob/2eafcd45dbd42f750df1ab9aa3770fed5cdf11ae/Libraries/react-native/react-native.js (the commit 2eafcd4#diff-cb910bd1233e7351becdc4aca5ad3688L121)

If that wouldn't help, the other two options I see are
https://github.com/jhabdas/react-native-webpack-starter-kit (by @jhabdas)
https://github.com/lelandrichardson/react-native-mock (by @lelandrichardson)

Hopefully, one of these options will facilitate testing in react-native (I'm using tape btw)

};


if (__DEV__) { if (__DEV__) {
ReactNative.addons.Perf = require('ReactDefaultPerf'); Object.defineProperty(ReactNative.addons, 'Perf', {
ReactNative.addons.TestUtils = require('ReactTestUtils'); enumerable: true,
get: () => require('ReactDefaultPerf'),
});
Object.defineProperty(ReactNative.addons, 'TestUtils', {
enumerable: true,
get: () => require('ReactTestUtils'),
});
} }


module.exports = ReactNative; module.exports = ReactNative;

3 comments on commit f9b744d

@chandu0101
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👏

@syzer
Copy link

@syzer syzer commented on f9b744d Dec 9, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@ThaJay
Copy link

@ThaJay ThaJay commented on f9b744d Feb 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why, but the option "cacheDirectory: true" in query and "externals: {'react-native': "require('react-native/Libraries/react-native/react-native.js')"}" in include solved that error for me.
thanks @jhabdas.

Please sign in to comment.