Skip to content

Commit da7c29f

Browse files
fairyswordmeta-codesync[bot]
authored andcommitted
Preserve lazy ViewManager initialization in UIManager (#58097)
Summary: - Preserve lazy ViewManager property descriptors when composing UIManager. - Avoid eagerly invoking lazy ViewManager getters and unnecessary getConstantsForViewManager calls during Bridgeless initialization. - Add regression coverage for lazy, own, enumerable and one-time getter behavior. - Platform scope: This targets the Android Bridgeless lazy ViewManager path. iOS does not currently expose this lazy getter path, so no iOS runtime behavior or performance change is expected. ## Changelog: [ANDROID] [FIXED] - Avoid unnecessary getConstantsForViewManager calls during UIManager initialization. Pull Request resolved: #58097 Test Plan: - `yarn flow-check` - `yarn test packages/react-native/Libraries/ReactNative --runInBand` - `yarn lint --quiet` - `yarn format-check` - Verified with Android RNTester offlineDebug that startup no longer triggers eager getConstantsForViewManager calls. Reviewed By: cipolleschi Differential Revision: D124967747 Pulled By: javache fbshipit-source-id: 1a4daf139b555ed266d5a7d3a0b90ebb02cfe5be
1 parent f99303c commit da7c29f

2 files changed

Lines changed: 93 additions & 5 deletions

File tree

‎packages/react-native/Libraries/ReactNative/UIManager.js‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @format
99
*/
1010

11+
import type {UnsafeObject} from '../Types/CodegenTypes';
1112
import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface';
1213

1314
import {getFabricUIManager} from './FabricUIManager';
@@ -23,9 +24,7 @@ const UIManagerImpl: UIManagerJSInterface =
2324
? require('./BridgelessUIManager').default
2425
: require('./PaperUIManager').default;
2526

26-
// $FlowFixMe[cannot-spread-interface]
27-
const UIManager: UIManagerJSInterface = {
28-
...UIManagerImpl,
27+
const UIManagerOverrides = {
2928
/**
3029
* Determines the location on screen, width, and height of the given view and
3130
* returns the values via an async callback. If successful, the callback will
@@ -129,7 +128,7 @@ const UIManager: UIManagerJSInterface = {
129128
measureLayout(
130129
reactTag: number,
131130
ancestorReactTag: number,
132-
errorCallback,
131+
errorCallback: (error: UnsafeObject) => void,
133132
callback: (
134133
left: number,
135134
top: number,
@@ -167,7 +166,7 @@ const UIManager: UIManagerJSInterface = {
167166

168167
measureLayoutRelativeToParent(
169168
reactTag: number,
170-
errorCallback,
169+
errorCallback: (error: UnsafeObject) => void,
171170
callback: (
172171
left: number,
173172
top: number,
@@ -249,4 +248,19 @@ const UIManager: UIManagerJSInterface = {
249248
},
250249
};
251250

251+
// Copy property descriptors instead of spreading UIManagerImpl. In Bridgeless
252+
// mode, UIManagerImpl may define enumerable lazy ViewManager getters. Spreading
253+
// the object would invoke every getter during module initialization.
254+
// $FlowFixMe[incompatible-type] Flow cannot infer properties from descriptors.
255+
const UIManager: UIManagerJSInterface = Object.create(
256+
Object.getPrototypeOf(UIManagerImpl),
257+
{
258+
...Object.getOwnPropertyDescriptors(
259+
// $FlowFixMe[class-object-subtyping] UIManagerImpl is a runtime object.
260+
UIManagerImpl,
261+
),
262+
...Object.getOwnPropertyDescriptors(UIManagerOverrides),
263+
},
264+
);
265+
252266
export default UIManager;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
jest.mock('../../Utilities/Platform', () => ({
14+
OS: 'android',
15+
select: spec => spec.android ?? spec.native ?? spec.default,
16+
}));
17+
jest.unmock('../UIManager');
18+
19+
describe('UIManager', () => {
20+
it('preserves lazy ViewManager getters when composing the Bridgeless implementation', () => {
21+
const originalBridgelessValue = global.RN$Bridgeless;
22+
const originalGetConstants = global.RN$LegacyInterop_UIManager_getConstants;
23+
const originalGetConstantsForViewManager =
24+
global.RN$LegacyInterop_UIManager_getConstantsForViewManager;
25+
const originalGetDefaultEventTypes =
26+
global.RN$LegacyInterop_UIManager_getDefaultEventTypes;
27+
const viewManagerConfig = {Commands: {focus: 1}};
28+
const getConstants = jest.fn(() => ({
29+
LazyViewManagersEnabled: true,
30+
ViewManagerNames: ['RCTTestView'],
31+
}));
32+
const getConstantsForViewManager = jest.fn(() => viewManagerConfig);
33+
34+
jest.resetModules();
35+
try {
36+
// $FlowExpectedError[cannot-write]
37+
global.RN$Bridgeless = true;
38+
// $FlowExpectedError[cannot-write]
39+
global.RN$LegacyInterop_UIManager_getConstants = getConstants;
40+
// $FlowExpectedError[cannot-write]
41+
global.RN$LegacyInterop_UIManager_getConstantsForViewManager =
42+
getConstantsForViewManager;
43+
// $FlowExpectedError[cannot-write]
44+
global.RN$LegacyInterop_UIManager_getDefaultEventTypes = jest.fn(
45+
() => ({}),
46+
);
47+
48+
const UIManager = require('../UIManager').default;
49+
50+
expect(getConstants).toHaveBeenCalledTimes(1);
51+
expect(getConstantsForViewManager).not.toHaveBeenCalled();
52+
expect(
53+
Object.getOwnPropertyDescriptor(UIManager, 'RCTTestView'),
54+
).toBeDefined();
55+
expect(Object.keys(UIManager)).toContain('RCTTestView');
56+
57+
expect(Reflect.get(UIManager, 'RCTTestView')).toBe(viewManagerConfig);
58+
expect(Reflect.get(UIManager, 'RCTTestView')).toBe(viewManagerConfig);
59+
expect(getConstantsForViewManager).toHaveBeenCalledTimes(1);
60+
} finally {
61+
// $FlowExpectedError[cannot-write]
62+
global.RN$Bridgeless = originalBridgelessValue;
63+
// $FlowExpectedError[cannot-write]
64+
global.RN$LegacyInterop_UIManager_getConstants = originalGetConstants;
65+
// $FlowExpectedError[cannot-write]
66+
global.RN$LegacyInterop_UIManager_getConstantsForViewManager =
67+
originalGetConstantsForViewManager;
68+
// $FlowExpectedError[cannot-write]
69+
global.RN$LegacyInterop_UIManager_getDefaultEventTypes =
70+
originalGetDefaultEventTypes;
71+
jest.resetModules();
72+
}
73+
});
74+
});

0 commit comments

Comments
 (0)