From 9c54b29b44d24f8f8090da9c7ebf569747a444df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Wed, 22 Mar 2023 17:54:36 +0000 Subject: [PATCH] Remove ReactFabricPublicInstance and used definition from ReactNativePrivateInterface (#26437) ## Summary Now that React Native owns the definition for public instances in Fabric and ReactNativePrivateInterface provides the methods to create instances and access private fields (see https://github.com/facebook/react-native/pull/36570), we can remove the definitions from React. After this PR, React Native public instances will be opaque types for React and it will only handle their creation but not their definition. This will make RN similar to DOM in how public instances are handled. This is a new version of #26418 which was closed without merging. ## How did you test this change? * Existing tests. * Manually synced the changes in this PR to React Native and tested it end to end in Meta's infra. --- .../src/ReactFabricHostConfig.js | 10 +- .../src/ReactFabricPublicInstance.js | 153 ----------- .../src/ReactFabricPublicInstanceUtils.js | 36 --- .../src/ReactNativeFiberInspector.js | 6 +- .../src/ReactNativePublicCompat.js | 11 +- .../ReactNativePrivateInterface.js | 11 + .../ReactPrivate/createPublicInstance.js | 21 ++ .../getNativeTagFromPublicInstance.js | 16 ++ .../ReactPrivate/getNodeFromPublicInstance.js | 20 ++ .../__tests__/ReactFabric-test.internal.js | 4 +- .../ReactFabricAndNative-test.internal.js | 2 +- .../ReactFabricHostComponent-test.internal.js | 248 ------------------ scripts/flow/react-native-host-hooks.js | 13 + 13 files changed, 96 insertions(+), 455 deletions(-) delete mode 100644 packages/react-native-renderer/src/ReactFabricPublicInstance.js delete mode 100644 packages/react-native-renderer/src/ReactFabricPublicInstanceUtils.js create mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/createPublicInstance.js create mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNativeTagFromPublicInstance.js create mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNodeFromPublicInstance.js delete mode 100644 packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index 0f4a59c77f6f0..5494f4152a47e 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -8,10 +8,6 @@ */ import type {TouchedViewDataAtPoint, ViewConfig} from './ReactNativeTypes'; -import { - createPublicInstance, - type ReactFabricHostComponent, -} from './ReactFabricPublicInstance'; import {create, diff} from './ReactNativeAttributePayload'; import {dispatchEvent} from './ReactFabricEventEmitter'; import { @@ -23,6 +19,8 @@ import { import { ReactNativeViewConfigRegistry, deepFreezeAndThrowOnMutationInDev, + createPublicInstance, + type PublicInstance as ReactNativePublicInstance, } from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; const { @@ -62,12 +60,12 @@ export type Instance = { // Reference to the React handle (the fiber) internalInstanceHandle: Object, // Exposed through refs. - publicInstance: ReactFabricHostComponent, + publicInstance: PublicInstance, }, }; export type TextInstance = {node: Node, ...}; export type HydratableInstance = Instance | TextInstance; -export type PublicInstance = ReactFabricHostComponent; +export type PublicInstance = ReactNativePublicInstance; export type Container = number; export type ChildSet = Object; export type HostContext = $ReadOnly<{ diff --git a/packages/react-native-renderer/src/ReactFabricPublicInstance.js b/packages/react-native-renderer/src/ReactFabricPublicInstance.js deleted file mode 100644 index 282e5af7cde1e..0000000000000 --- a/packages/react-native-renderer/src/ReactFabricPublicInstance.js +++ /dev/null @@ -1,153 +0,0 @@ -/** - * 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 - */ - -import type {ElementRef} from 'react'; -import type { - ViewConfig, - INativeMethods, - HostComponent, - MeasureInWindowOnSuccessCallback, - MeasureLayoutOnSuccessCallback, - MeasureOnSuccessCallback, -} from './ReactNativeTypes'; - -import {TextInputState} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; -import {create} from './ReactNativeAttributePayload'; -import {warnForStyleProps} from './NativeMethodsMixinUtils'; -import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat'; - -const { - measure: fabricMeasure, - measureInWindow: fabricMeasureInWindow, - measureLayout: fabricMeasureLayout, - setNativeProps, - getBoundingClientRect: fabricGetBoundingClientRect, -} = nativeFabricUIManager; - -const noop = () => {}; - -/** - * This is used for refs on host components. - */ -export class ReactFabricHostComponent implements INativeMethods { - // These need to be accessible from `ReactFabricPublicInstanceUtils`. - __nativeTag: number; - __internalInstanceHandle: mixed; - - _viewConfig: ViewConfig; - - constructor( - tag: number, - viewConfig: ViewConfig, - internalInstanceHandle: mixed, - ) { - this.__nativeTag = tag; - this._viewConfig = viewConfig; - this.__internalInstanceHandle = internalInstanceHandle; - } - - blur() { - TextInputState.blurTextInput(this); - } - - focus() { - TextInputState.focusTextInput(this); - } - - measure(callback: MeasureOnSuccessCallback) { - const node = getNodeFromInternalInstanceHandle( - this.__internalInstanceHandle, - ); - if (node != null) { - fabricMeasure(node, callback); - } - } - - measureInWindow(callback: MeasureInWindowOnSuccessCallback) { - const node = getNodeFromInternalInstanceHandle( - this.__internalInstanceHandle, - ); - if (node != null) { - fabricMeasureInWindow(node, callback); - } - } - - measureLayout( - relativeToNativeNode: number | ElementRef>, - onSuccess: MeasureLayoutOnSuccessCallback, - onFail?: () => void /* currently unused */, - ) { - if ( - typeof relativeToNativeNode === 'number' || - !(relativeToNativeNode instanceof ReactFabricHostComponent) - ) { - if (__DEV__) { - console.error( - 'Warning: ref.measureLayout must be called with a ref to a native component.', - ); - } - - return; - } - - const toStateNode = getNodeFromInternalInstanceHandle( - this.__internalInstanceHandle, - ); - const fromStateNode = getNodeFromInternalInstanceHandle( - relativeToNativeNode.__internalInstanceHandle, - ); - - if (toStateNode != null && fromStateNode != null) { - fabricMeasureLayout( - toStateNode, - fromStateNode, - onFail != null ? onFail : noop, - onSuccess != null ? onSuccess : noop, - ); - } - } - - unstable_getBoundingClientRect(): DOMRect { - const node = getNodeFromInternalInstanceHandle( - this.__internalInstanceHandle, - ); - if (node != null) { - const rect = fabricGetBoundingClientRect(node); - - if (rect) { - return new DOMRect(rect[0], rect[1], rect[2], rect[3]); - } - } - - // Empty rect if any of the above failed - return new DOMRect(0, 0, 0, 0); - } - - setNativeProps(nativeProps: {...}): void { - if (__DEV__) { - warnForStyleProps(nativeProps, this._viewConfig.validAttributes); - } - const updatePayload = create(nativeProps, this._viewConfig.validAttributes); - - const node = getNodeFromInternalInstanceHandle( - this.__internalInstanceHandle, - ); - if (node != null && updatePayload != null) { - setNativeProps(node, updatePayload); - } - } -} - -export function createPublicInstance( - tag: number, - viewConfig: ViewConfig, - internalInstanceHandle: mixed, -): ReactFabricHostComponent { - return new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle); -} diff --git a/packages/react-native-renderer/src/ReactFabricPublicInstanceUtils.js b/packages/react-native-renderer/src/ReactFabricPublicInstanceUtils.js deleted file mode 100644 index 203f96589fb7f..0000000000000 --- a/packages/react-native-renderer/src/ReactFabricPublicInstanceUtils.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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 - */ - -import type {ReactFabricHostComponent} from './ReactFabricPublicInstance'; -import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat'; - -/** - * IMPORTANT: This module is used in Paper and Fabric. It needs to be defined - * outside of `ReactFabricPublicInstance` because that module requires - * `nativeFabricUIManager` to be defined in the global scope (which does not - * happen in Paper). - */ - -export function getNativeTagFromPublicInstance( - publicInstance: ReactFabricHostComponent, -): number { - return publicInstance.__nativeTag; -} - -export function getNodeFromPublicInstance( - publicInstance: ReactFabricHostComponent, -): mixed { - if (publicInstance.__internalInstanceHandle == null) { - return null; - } - - return getNodeFromInternalInstanceHandle( - publicInstance.__internalInstanceHandle, - ); -} diff --git a/packages/react-native-renderer/src/ReactNativeFiberInspector.js b/packages/react-native-renderer/src/ReactNativeFiberInspector.js index 38a3855ea92a3..d1884c44eafc5 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberInspector.js +++ b/packages/react-native-renderer/src/ReactNativeFiberInspector.js @@ -17,10 +17,12 @@ import { import getComponentNameFromType from 'shared/getComponentNameFromType'; import {HostComponent} from 'react-reconciler/src/ReactWorkTags'; // Module provided by RN: -import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; +import { + UIManager, + getNodeFromPublicInstance, +} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; import {enableGetInspectorDataForInstanceInProduction} from 'shared/ReactFeatureFlags'; import {getClosestInstanceFromNode} from './ReactNativeComponentTree'; -import {getNodeFromPublicInstance} from './ReactFabricPublicInstanceUtils'; import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat'; const emptyObject = {}; diff --git a/packages/react-native-renderer/src/ReactNativePublicCompat.js b/packages/react-native-renderer/src/ReactNativePublicCompat.js index 569397b4e8347..4a8452ad3550f 100644 --- a/packages/react-native-renderer/src/ReactNativePublicCompat.js +++ b/packages/react-native-renderer/src/ReactNativePublicCompat.js @@ -14,6 +14,8 @@ import type {ElementRef, ElementType} from 'react'; import { UIManager, legacySendAccessibilityEvent, + getNodeFromPublicInstance, + getNativeTagFromPublicInstance, } from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; import { @@ -23,11 +25,6 @@ import { import ReactSharedInternals from 'shared/ReactSharedInternals'; import getComponentNameFromType from 'shared/getComponentNameFromType'; -import { - getNodeFromPublicInstance, - getNativeTagFromPublicInstance, -} from './ReactFabricPublicInstanceUtils'; - const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; export function findHostInstance_DEPRECATED( @@ -83,6 +80,7 @@ export function findHostInstance_DEPRECATED( // findHostInstance handles legacy vs. Fabric differences correctly // $FlowFixMe[incompatible-exact] we need to fix the definition of `HostComponent` to use NativeMethods as an interface, not as a type. + // $FlowFixMe[incompatible-return] return hostInstance; } @@ -147,9 +145,8 @@ export function findNodeHandle(componentOrHandle: any): ?number { return hostInstance; } - // $FlowFixMe[prop-missing] For compatibility with legacy renderer instances + // $FlowFixMe[incompatible-type] For compatibility with legacy renderer instances if (hostInstance._nativeTag != null) { - // $FlowFixMe[incompatible-return] return hostInstance._nativeTag; } diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js index befc3bb617f4f..03b89b3e711f3 100644 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js +++ b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js @@ -7,6 +7,8 @@ * @flow strict-local */ +export opaque type PublicInstance = mixed; + module.exports = { get BatchedBridge() { return require('./BatchedBridge.js'); @@ -44,4 +46,13 @@ module.exports = { get RawEventEmitter() { return require('./RawEventEmitter').default; }, + get getNativeTagFromPublicInstance() { + return require('./getNativeTagFromPublicInstance').default; + }, + get getNodeFromPublicInstance() { + return require('./getNodeFromPublicInstance').default; + }, + get createPublicInstance() { + return require('./createPublicInstance').default; + }, }; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/createPublicInstance.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/createPublicInstance.js new file mode 100644 index 0000000000000..4f16c9d9ff109 --- /dev/null +++ b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/createPublicInstance.js @@ -0,0 +1,21 @@ +/** + * 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 + */ + +import type {PublicInstance} from './ReactNativePrivateInterface'; + +export default function createPublicInstance( + tag: number, + viewConfig: mixed, + internalInstanceHandle: mixed, +): PublicInstance { + return { + __nativeTag: tag, + __internalInstanceHandle: internalInstanceHandle, + }; +} diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNativeTagFromPublicInstance.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNativeTagFromPublicInstance.js new file mode 100644 index 0000000000000..b6862281494c5 --- /dev/null +++ b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNativeTagFromPublicInstance.js @@ -0,0 +1,16 @@ +/** + * 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 + */ + +import type {PublicInstance} from './ReactNativePrivateInterface'; + +export default function getNativeTagFromPublicInstance( + publicInstance: PublicInstance, +) { + return publicInstance.__nativeTag; +} diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNodeFromPublicInstance.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNodeFromPublicInstance.js new file mode 100644 index 0000000000000..b592968462ed4 --- /dev/null +++ b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/getNodeFromPublicInstance.js @@ -0,0 +1,20 @@ +/** + * 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 + */ + +import type {PublicInstance} from './ReactNativePrivateInterface'; + +import {getNodeFromInternalInstanceHandle} from '../../../../ReactNativePublicCompat'; + +export default function getNodeFromPublicInstance( + publicInstance: PublicInstance, +) { + return getNodeFromInternalInstanceHandle( + publicInstance.__internalInstanceHandle, + ); +} diff --git a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js index 72377b7d12cc5..cdc8882a08554 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js @@ -43,9 +43,9 @@ describe('ReactFabric', () => { require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') .ReactNativeViewConfigRegistry.register; getNativeTagFromPublicInstance = - require('../ReactFabricPublicInstanceUtils').getNativeTagFromPublicInstance; + require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').getNativeTagFromPublicInstance; getNodeFromPublicInstance = - require('../ReactFabricPublicInstanceUtils').getNodeFromPublicInstance; + require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').getNodeFromPublicInstance; act = require('internal-test-utils').act; }); diff --git a/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js index fca6d00c72774..4e0fcad9c80c3 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js @@ -37,7 +37,7 @@ describe('created with ReactFabric called with ReactNative', () => { require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') .ReactNativeViewConfigRegistry.register; getNativeTagFromPublicInstance = - require('../ReactFabricPublicInstanceUtils').getNativeTagFromPublicInstance; + require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').getNativeTagFromPublicInstance; }); it('find Fabric instances with the RN renderer', () => { diff --git a/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js deleted file mode 100644 index ceab02d8497be..0000000000000 --- a/packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * @emails react-core - * @jest-environment node - */ - -'use strict'; - -import * as React from 'react'; - -beforeEach(() => { - jest.resetModules(); - jest.restoreAllMocks(); - - require('react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager'); -}); - -/** - * Renders a sequence of mock views as dictated by `keyLists`. The `keyLists` - * argument is an array of arrays which determines the number of render passes, - * how many views will be rendered in each pass, and what the keys are for each - * of the views. - * - * If an element in `keyLists` is null, the entire root will be unmounted. - * - * The return value is an array of arrays with the resulting refs from rendering - * each corresponding array of keys. - * - * If the corresponding array of keys is null, the returned element at that - * index will also be null. - */ -async function mockRenderKeys(keyLists) { - const ReactFabric = require('react-native-renderer/fabric'); - const createReactNativeComponentClass = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') - .ReactNativeViewConfigRegistry.register; - const act = require('internal-test-utils').act; - - const mockContainerTag = 11; - const MockView = createReactNativeComponentClass('RCTMockView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTMockView', - })); - - const result = []; - for (let i = 0; i < keyLists.length; i++) { - const keyList = keyLists[i]; - if (Array.isArray(keyList)) { - const refs = keyList.map(key => undefined); - await act(() => { - ReactFabric.render( - - {keyList.map((key, index) => ( - { - refs[index] = ref; - }} - /> - ))} - , - mockContainerTag, - ); - }); - // Clone `refs` to ignore future passes. - result.push([...refs]); - continue; - } - if (keyList == null) { - await act(() => { - ReactFabric.stopSurface(mockContainerTag); - }); - result.push(null); - continue; - } - throw new TypeError( - `Invalid 'keyLists' element of type ${typeof keyList}.`, - ); - } - - return result; -} - -describe('blur', () => { - test('blur() invokes TextInputState', async () => { - const { - TextInputState, - } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - - const [[fooRef]] = await mockRenderKeys([['foo']]); - - fooRef.blur(); - - expect(TextInputState.blurTextInput.mock.calls).toEqual([[fooRef]]); - }); -}); - -describe('focus', () => { - test('focus() invokes TextInputState', async () => { - const { - TextInputState, - } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - - const [[fooRef]] = await mockRenderKeys([['foo']]); - - fooRef.focus(); - - expect(TextInputState.focusTextInput.mock.calls).toEqual([[fooRef]]); - }); -}); - -describe('measure', () => { - test('component.measure(...) invokes callback', async () => { - const [[fooRef]] = await mockRenderKeys([['foo']]); - - const callback = jest.fn(); - fooRef.measure(callback); - - expect(nativeFabricUIManager.measure).toHaveBeenCalledTimes(1); - expect(callback.mock.calls).toEqual([[10, 10, 100, 100, 0, 0]]); - }); - - test('unmounted.measure(...) does nothing', async () => { - const [[fooRef]] = await mockRenderKeys([['foo'], null]); - - const callback = jest.fn(); - fooRef.measure(callback); - - expect(nativeFabricUIManager.measure).not.toHaveBeenCalled(); - expect(callback).not.toHaveBeenCalled(); - }); -}); - -describe('measureInWindow', () => { - test('component.measureInWindow(...) invokes callback', async () => { - const [[fooRef]] = await mockRenderKeys([['foo']]); - - const callback = jest.fn(); - fooRef.measureInWindow(callback); - - expect(nativeFabricUIManager.measureInWindow).toHaveBeenCalledTimes(1); - expect(callback.mock.calls).toEqual([[10, 10, 100, 100]]); - }); - - test('unmounted.measureInWindow(...) does nothing', async () => { - const [[fooRef]] = await mockRenderKeys([['foo'], null]); - - const callback = jest.fn(); - fooRef.measureInWindow(callback); - - expect(nativeFabricUIManager.measureInWindow).not.toHaveBeenCalled(); - expect(callback).not.toHaveBeenCalled(); - }); -}); - -describe('measureLayout', () => { - test('component.measureLayout(component, ...) invokes callback', async () => { - const [[fooRef, barRef]] = await mockRenderKeys([['foo', 'bar']]); - - const successCallback = jest.fn(); - const failureCallback = jest.fn(); - fooRef.measureLayout(barRef, successCallback, failureCallback); - - expect(nativeFabricUIManager.measureLayout).toHaveBeenCalledTimes(1); - expect(successCallback.mock.calls).toEqual([[1, 1, 100, 100]]); - }); - - test('unmounted.measureLayout(component, ...) does nothing', async () => { - const [[fooRef, barRef]] = await mockRenderKeys([ - ['foo', 'bar'], - ['foo', null], - ]); - - const successCallback = jest.fn(); - const failureCallback = jest.fn(); - fooRef.measureLayout(barRef, successCallback, failureCallback); - - expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled(); - expect(successCallback).not.toHaveBeenCalled(); - }); - - test('component.measureLayout(unmounted, ...) does nothing', async () => { - const [[fooRef, barRef]] = await mockRenderKeys([ - ['foo', 'bar'], - [null, 'bar'], - ]); - - const successCallback = jest.fn(); - const failureCallback = jest.fn(); - fooRef.measureLayout(barRef, successCallback, failureCallback); - - expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled(); - expect(successCallback).not.toHaveBeenCalled(); - }); - - test('unmounted.measureLayout(unmounted, ...) does nothing', async () => { - const [[fooRef, barRef]] = await mockRenderKeys([['foo', 'bar'], null]); - - const successCallback = jest.fn(); - const failureCallback = jest.fn(); - fooRef.measureLayout(barRef, successCallback, failureCallback); - - expect(nativeFabricUIManager.measureLayout).not.toHaveBeenCalled(); - expect(successCallback).not.toHaveBeenCalled(); - }); -}); - -describe('unstable_getBoundingClientRect', () => { - test('component.unstable_getBoundingClientRect() returns DOMRect', async () => { - const [[fooRef]] = await mockRenderKeys([['foo']]); - - const rect = fooRef.unstable_getBoundingClientRect(); - - expect(nativeFabricUIManager.getBoundingClientRect).toHaveBeenCalledTimes( - 1, - ); - expect(rect.toJSON()).toMatchObject({ - x: 10, - y: 10, - width: 100, - height: 100, - }); - }); - - test('unmounted.unstable_getBoundingClientRect() returns empty DOMRect', async () => { - const [[fooRef]] = await mockRenderKeys([['foo'], null]); - - const rect = fooRef.unstable_getBoundingClientRect(); - - expect(nativeFabricUIManager.getBoundingClientRect).not.toHaveBeenCalled(); - expect(rect.toJSON()).toMatchObject({x: 0, y: 0, width: 0, height: 0}); - }); -}); - -describe('setNativeProps', () => { - test('setNativeProps(...) invokes setNativeProps on Fabric UIManager', async () => { - const { - UIManager, - } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - - const [[fooRef]] = await mockRenderKeys([['foo']]); - fooRef.setNativeProps({foo: 'baz'}); - - expect(UIManager.updateView).not.toBeCalled(); - expect(nativeFabricUIManager.setNativeProps).toHaveBeenCalledTimes(1); - }); -}); diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js index 7cb3e389126c5..d3f5d3b7b50c5 100644 --- a/scripts/flow/react-native-host-hooks.js +++ b/scripts/flow/react-native-host-hooks.js @@ -16,6 +16,7 @@ type __MeasureInWindowOnSuccessCallback = any; type __MeasureLayoutOnSuccessCallback = any; type __ReactNativeBaseComponentViewConfig = any; type __ViewConfigGetter = any; +type __ViewConfig = any; // libdefs cannot actually import. This is supposed to be the type imported // from 'react-native-renderer/src/legacy-events/TopLevelEventTypes'; @@ -143,6 +144,18 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface' emit: (channel: string, event: RawEventEmitterEvent) => string, ... }; + declare export opaque type PublicInstance; + declare export function getNodeFromPublicInstance( + publicInstance: PublicInstance, + ): Object; + declare export function getNativeTagFromPublicInstance( + publicInstance: PublicInstance, + ): number; + declare export function createPublicInstance( + tag: number, + viewConfig: __ViewConfig, + internalInstanceHandle: mixed, + ): PublicInstance; } declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInitializeCore' {