diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js new file mode 100644 index 00000000000..e4d7812345b --- /dev/null +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js @@ -0,0 +1,171 @@ +/** + * 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. + * + * @format + * @flow strict-local + */ + +import type { + AttributeConfiguration, + HostComponent, + INativeMethods, + MeasureInWindowOnSuccessCallback, + MeasureLayoutOnSuccessCallback, + MeasureOnSuccessCallback, + ViewConfig, +} from '../../Renderer/shims/ReactNativeTypes'; +import type {ElementRef} from 'react'; + +import TextInputState from '../../Components/TextInput/TextInputState'; +import {getNodeFromInternalInstanceHandle} from '../../Renderer/shims/ReactFabric'; +import {getFabricUIManager} from '../FabricUIManager'; +import {create} from './ReactNativeAttributePayload'; +import nullthrows from 'nullthrows'; + +const { + measure: fabricMeasure, + measureInWindow: fabricMeasureInWindow, + measureLayout: fabricMeasureLayout, + getBoundingClientRect: fabricGetBoundingClientRect, + setNativeProps, +} = nullthrows(getFabricUIManager()); + +const noop = () => {}; + +/** + * This is used for refs on host components. + */ +export default 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() { + // $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this. + TextInputState.blurTextInput(this); + } + + focus() { + // $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this. + 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); + } + } +} + +function warnForStyleProps( + props: {...}, + validAttributes: AttributeConfiguration, +): void { + if (__DEV__) { + for (const key in validAttributes.style) { + if (!(validAttributes[key] || props[key] === undefined)) { + console.error( + 'You are setting the style `{ %s' + + ': ... }` as a prop. You ' + + 'should nest it in a style object. ' + + 'E.g. `{ style: { %s' + + ': ... } }`', + key, + key, + ); + } + } + } +} diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js index 93253d4e702..b1945fffa1b 100644 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance.js @@ -8,173 +8,23 @@ * @flow strict-local */ -import type { - AttributeConfiguration, - HostComponent, - INativeMethods, - MeasureInWindowOnSuccessCallback, - MeasureLayoutOnSuccessCallback, - MeasureOnSuccessCallback, - ViewConfig, -} from '../../Renderer/shims/ReactNativeTypes'; -import type {ElementRef} from 'react'; +import typeof ReactFabricType from '../../Renderer/shims/ReactFabric'; +import type {ViewConfig} from '../../Renderer/shims/ReactNativeTypes'; +import type ReactFabricHostComponentType from './ReactFabricHostComponent'; -import TextInputState from '../../Components/TextInput/TextInputState'; -import {getNodeFromInternalInstanceHandle} from '../../Renderer/shims/ReactFabric'; -import {getFabricUIManager} from '../FabricUIManager'; -import {create} from './ReactNativeAttributePayload'; -import nullthrows from 'nullthrows'; - -const { - measure: fabricMeasure, - measureInWindow: fabricMeasureInWindow, - measureLayout: fabricMeasureLayout, - getBoundingClientRect: fabricGetBoundingClientRect, - setNativeProps, -} = nullthrows(getFabricUIManager()); - -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() { - // $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this. - TextInputState.blurTextInput(this); - } - - focus() { - // $FlowFixMe[incompatible-exact] Migrate all usages of `NativeMethods` to an interface to fix this. - 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 warnForStyleProps( - props: {...}, - validAttributes: AttributeConfiguration, -): void { - if (__DEV__) { - for (const key in validAttributes.style) { - if (!(validAttributes[key] || props[key] === undefined)) { - console.error( - 'You are setting the style `{ %s' + - ': ... }` as a prop. You ' + - 'should nest it in a style object. ' + - 'E.g. `{ style: { %s' + - ': ... } }`', - key, - key, - ); - } - } - } -} +// Lazy loaded to avoid evaluating the module when using the legacy renderer. +let ReactFabricHostComponent: Class; +// Lazy loaded to avoid evaluating the module when using the legacy renderer. +let ReactFabric: ReactFabricType; export function createPublicInstance( tag: number, viewConfig: ViewConfig, internalInstanceHandle: mixed, -): ReactFabricHostComponent { +): ReactFabricHostComponentType { + if (ReactFabricHostComponent == null) { + ReactFabricHostComponent = require('./ReactFabricHostComponent').default; + } return new ReactFabricHostComponent(tag, viewConfig, internalInstanceHandle); } @@ -186,15 +36,24 @@ export function createPublicTextInstance(internalInstanceHandle: mixed): {} { } export function getNativeTagFromPublicInstance( - publicInstance: ReactFabricHostComponent, + publicInstance: ReactFabricHostComponentType, ): number { return publicInstance.__nativeTag; } export function getNodeFromPublicInstance( - publicInstance: ReactFabricHostComponent, + publicInstance: ReactFabricHostComponentType, ): mixed { - return getNodeFromInternalInstanceHandle( + // Avoid loading ReactFabric if using an instance from the legacy renderer. + if (publicInstance.__internalInstanceHandle == null) { + return null; + } + + if (ReactFabric == null) { + ReactFabric = require('../../Renderer/shims/ReactFabric'); + } + + return ReactFabric.getNodeFromInternalInstanceHandle( publicInstance.__internalInstanceHandle, ); }