Skip to content

Commit

Permalink
Avoid view manager class loads
Browse files Browse the repository at this point in the history
Summary:
We are currently iterating through each view manager to get its class name to pass to JS. JS uses this list to define lazy property accesses for each view manager to grab the constants synchronously. This results in each view manager's class loading immediately -- causing a small perf hit.

Let's avoid this view managers list entirely. JS is able to access each view manager directly by calling getConstantsForViewManager(name)

Reviewed By: TheSavior

Differential Revision: D9930713

fbshipit-source-id: 4aa013f8398d4f51b7eef07937d2977ba1950726
  • Loading branch information
ayc1 authored and facebook-github-bot committed Sep 27, 2018
1 parent 230b7b0 commit 95174d4
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 5 deletions.
34 changes: 33 additions & 1 deletion Libraries/ReactNative/UIManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

const NativeModules = require('NativeModules');
const Platform = require('Platform');
const UIManagerProperties = require('UIManagerProperties');

const defineLazyObjectProperty = require('defineLazyObjectProperty');
const invariant = require('fbjs/lib/invariant');

const {UIManager} = NativeModules;
const viewManagerConfigs = {};

invariant(
UIManager,
Expand All @@ -36,7 +38,20 @@ UIManager.takeSnapshot = function() {
);
};
UIManager.getViewManagerConfig = function(viewManagerName: string) {
return UIManager[viewManagerName];
if (
viewManagerConfigs[viewManagerName] === undefined &&
UIManager.getConstantsForViewManager
) {
try {
viewManagerConfigs[
viewManagerName
] = UIManager.getConstantsForViewManager(viewManagerName);
} catch (e) {
viewManagerConfigs[viewManagerName] = null;
}
}

return viewManagerConfigs[viewManagerName];
};

/**
Expand All @@ -48,6 +63,7 @@ if (Platform.OS === 'ios') {
Object.keys(UIManager).forEach(viewName => {
const viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
viewManagerConfigs[viewName] = viewConfig;
defineLazyObjectProperty(viewConfig, 'Constants', {
get: () => {
const viewManager = NativeModules[viewConfig.Manager];
Expand Down Expand Up @@ -107,4 +123,20 @@ if (Platform.OS === 'ios') {
if (global.__makePartial) global.__makePartial(UIManager);
}

if (__DEV__) {
Object.keys(UIManager).forEach(viewManagerName => {
if (!UIManagerProperties.includes(viewManagerName)) {
defineLazyObjectProperty(UIManager, viewManagerName, {
get: () => {
console.warn(
`Accessing view manager configs directly off UIManager via UIManager['${viewManagerName}'] ` +
`is no longer supported. Use UIManager.getViewManager('${viewManagerName}') instead.`,
);
return UIManager.getViewManagerConfig(viewManagerName);
},
});
}
});
}

module.exports = UIManager;
67 changes: 67 additions & 0 deletions Libraries/ReactNative/UIManagerProperties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
'use strict';

/**
* The list of non-ViewManager related UIManager properties.
*
* In an effort to improve startup performance by lazily loading view managers,
* the interface to access view managers will change from
* UIManager['viewManagerName'] to UIManager.getViewManager('viewManagerName').
* By using a function call instead of a property access, the UIManager will
* be able to initialize and load the required view manager from native
* synchronously. All of React Native's core components have been updated to
* use getViewManager(). For the next few releases, any usage of
* UIManager['viewManagerName'] will result in a warning. Because React Native
* does not support Proxy objects, a view manager access is implied if any of
* UIManager's properties that are not one of the properties below is being
* accessed. Once UIManager property accesses for view managers has been fully
* deprecated, this file will also be removed.
*/
module.exports = [
'clearJSResponder',
'configureNextLayoutAnimation',
'createView',
'dismissPopupMenu',
'dispatchViewManagerCommand',
'findSubviewIn',
'getConstantsForViewManager',
'getDefaultEventTypes',
'manageChildren',
'measure',
'measureInWindow',
'measureLayout',
'measureLayoutRelativeToParent',
'playTouchSound',
'removeRootView',
'removeSubviewsFromContainerWithID',
'replaceExistingNonRootView',
'sendAccessibilityEvent',
'setChildren',
'setJSResponder',
'setLayoutAnimationEnabledExperimental',
'showPopupMenu',
'updateView',
'viewIsDescendantOf',
'PopupMenu',
'LazyViewManagersEnabled',
'ViewManagerNames',
'StyleConstants',
'AccessibilityEventTypes',
'UIView',
'__takeSnapshot',
'takeSnapshot',
'getViewManagerConfig',
'measureViewsInRect',
'blur',
'focus',
'genericBubblingEventTypes',
'genericDirectEventTypes',
];
2 changes: 1 addition & 1 deletion Libraries/ReactNative/getNativeComponentAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function attachDefaultEventTypes(viewConfig: any) {
// This is supported on UIManager platforms (ex: Android),
// as lazy view managers are not implemented for all platforms.
// See [UIManager] for details on constants and implementations.
if (UIManager.ViewManagerNames) {
if (UIManager.ViewManagerNames || UIManager.LazyViewManagersEnabled) {
// Lazy view managers enabled.
viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ rn_android_library(
react_native_target("java/com/facebook/react/animation:animation"),
react_native_target("java/com/facebook/react/bridge:bridge"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/common:common"),
react_native_target("java/com/facebook/react/config:config"),
react_native_target("java/com/facebook/react/module/annotations:annotations"),
react_native_target("java/com/facebook/react/modules/core:core"),
react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;

import com.facebook.react.common.MapBuilder;
import com.facebook.systrace.Systrace;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.systrace.SystraceMessage;
import java.util.List;
import java.util.Map;
Expand All @@ -35,7 +35,10 @@
/* package */ static Map<String, Object> createConstants(
UIManagerModule.ViewManagerResolver resolver) {
Map<String, Object> constants = UIManagerModuleConstants.getConstants();
constants.put("ViewManagerNames", resolver.getViewManagerNames());
if (!ReactFeatureFlags.lazilyLoadViewManagers) {
constants.put("ViewManagerNames", resolver.getViewManagerNames());
}
constants.put("LazyViewManagersEnabled", true);
return constants;
}

Expand Down

0 comments on commit 95174d4

Please sign in to comment.