Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions packages/react-native/Libraries/ReactNative/UIManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ const UIManagerImpl: UIManagerJSInterface =
? require('./BridgelessUIManager').default
: require('./PaperUIManager').default;

// $FlowFixMe[cannot-spread-interface]
const UIManager: UIManagerJSInterface = {
...UIManagerImpl,
const UIManagerOverrides = {
/**
* Determines the location on screen, width, and height of the given view and
* returns the values via an async callback. If successful, the callback will
Expand Down Expand Up @@ -243,4 +241,19 @@ const UIManager: UIManagerJSInterface = {
},
};

// Copy property descriptors instead of spreading UIManagerImpl. In Bridgeless
// mode, UIManagerImpl may define enumerable lazy ViewManager getters. Spreading
// the object would invoke every getter during module initialization.
// $FlowFixMe[incompatible-type] Flow cannot infer properties from descriptors.
const UIManager: UIManagerJSInterface = Object.create(
Object.getPrototypeOf(UIManagerImpl),
{
...Object.getOwnPropertyDescriptors(
// $FlowFixMe[class-object-subtyping] UIManagerImpl is a runtime object.
UIManagerImpl,
),
...Object.getOwnPropertyDescriptors(UIManagerOverrides),
},
);

export default UIManager;
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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';

jest.mock('../../Utilities/Platform', () => ({
OS: 'android',
select: spec => spec.android ?? spec.native ?? spec.default,
}));
jest.unmock('../UIManager');

describe('UIManager', () => {
it('preserves lazy ViewManager getters when composing the Bridgeless implementation', () => {
const originalBridgelessValue = global.RN$Bridgeless;
const originalGetConstants = global.RN$LegacyInterop_UIManager_getConstants;
const originalGetConstantsForViewManager =
global.RN$LegacyInterop_UIManager_getConstantsForViewManager;
const originalGetDefaultEventTypes =
global.RN$LegacyInterop_UIManager_getDefaultEventTypes;
const viewManagerConfig = {Commands: {focus: 1}};
const getConstants = jest.fn(() => ({
LazyViewManagersEnabled: true,
ViewManagerNames: ['RCTTestView'],
}));
const getConstantsForViewManager = jest.fn(() => viewManagerConfig);

jest.resetModules();
try {
// $FlowExpectedError[cannot-write]
global.RN$Bridgeless = true;
// $FlowExpectedError[cannot-write]
global.RN$LegacyInterop_UIManager_getConstants = getConstants;
// $FlowExpectedError[cannot-write]
global.RN$LegacyInterop_UIManager_getConstantsForViewManager =
getConstantsForViewManager;
// $FlowExpectedError[cannot-write]
global.RN$LegacyInterop_UIManager_getDefaultEventTypes = jest.fn(
() => ({}),
);

const UIManager = require('../UIManager').default;

expect(getConstants).toHaveBeenCalledTimes(1);
expect(getConstantsForViewManager).not.toHaveBeenCalled();
expect(
Object.getOwnPropertyDescriptor(UIManager, 'RCTTestView'),
).toBeDefined();
expect(Object.keys(UIManager)).toContain('RCTTestView');

expect(Reflect.get(UIManager, 'RCTTestView')).toBe(viewManagerConfig);
expect(Reflect.get(UIManager, 'RCTTestView')).toBe(viewManagerConfig);
expect(getConstantsForViewManager).toHaveBeenCalledTimes(1);
} finally {
// $FlowExpectedError[cannot-write]
global.RN$Bridgeless = originalBridgelessValue;
// $FlowExpectedError[cannot-write]
global.RN$LegacyInterop_UIManager_getConstants = originalGetConstants;
// $FlowExpectedError[cannot-write]
global.RN$LegacyInterop_UIManager_getConstantsForViewManager =
originalGetConstantsForViewManager;
// $FlowExpectedError[cannot-write]
global.RN$LegacyInterop_UIManager_getDefaultEventTypes =
originalGetDefaultEventTypes;
jest.resetModules();
}
});
});
Loading