From 4bb596b6211f3f04acdb469dd1e0411894a55a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 3 Mar 2023 12:15:47 +0000 Subject: [PATCH 1/2] Remove unnecessary (and incorrect) code for compatibility with Paper in the Fabric version of GlobalResponderHandler --- .../src/ReactFabricGlobalResponderHandler.js | 48 ++++++------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/packages/react-native-renderer/src/ReactFabricGlobalResponderHandler.js b/packages/react-native-renderer/src/ReactFabricGlobalResponderHandler.js index 90b94295a09f4..10ac51d4317d6 100644 --- a/packages/react-native-renderer/src/ReactFabricGlobalResponderHandler.js +++ b/packages/react-native-renderer/src/ReactFabricGlobalResponderHandler.js @@ -7,42 +7,24 @@ * @flow */ -// Module provided by RN: -import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - const ReactFabricGlobalResponderHandler = { onChange: function (from: any, to: any, blockNativeResponder: boolean) { - const fromOrTo = from || to; - const fromOrToStateNode = fromOrTo && fromOrTo.stateNode; - const isFabric = !!( - fromOrToStateNode && fromOrToStateNode.canonical._internalInstanceHandle - ); - - if (isFabric) { - if (from) { - // equivalent to clearJSResponder - nativeFabricUIManager.setIsJSResponder( - from.stateNode.node, - false, - blockNativeResponder || false, - ); - } + if (from) { + // equivalent to clearJSResponder + nativeFabricUIManager.setIsJSResponder( + from.stateNode.node, + false, + blockNativeResponder || false, + ); + } - if (to) { - // equivalent to setJSResponder - nativeFabricUIManager.setIsJSResponder( - to.stateNode.node, - true, - blockNativeResponder || false, - ); - } - } else { - if (to !== null) { - const tag = to.stateNode.canonical._nativeTag; - UIManager.setJSResponder(tag, blockNativeResponder); - } else { - UIManager.clearJSResponder(); - } + if (to) { + // equivalent to setJSResponder + nativeFabricUIManager.setIsJSResponder( + to.stateNode.node, + true, + blockNativeResponder || false, + ); } }, }; From b2a5c00d6197ae2074eb49a543128d831af8d95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 3 Mar 2023 13:59:10 +0000 Subject: [PATCH 2/2] Extracted definition and access to public instances to a separate module in Fabric --- .../src/NativeMethodsMixinUtils.js | 20 -- .../react-native-renderer/src/ReactFabric.js | 45 +++-- .../src/ReactFabricComponentTree.js | 53 ++++-- .../src/ReactFabricEventEmitter.js | 4 +- .../src/ReactFabricHostConfig.js | 179 ++++-------------- .../src/ReactFabricPublicInstance.js | 163 ++++++++++++++++ .../src/ReactFabricPublicInstanceUtils.js | 23 +++ .../src/ReactNativeComponentTree.js | 7 +- .../src/ReactNativeFiberInspector.js | 5 +- .../src/ReactNativeHostConfig.js | 4 +- .../src/ReactNativeRenderer.js | 46 ++++- .../src/ReactNativeTypes.js | 12 -- 12 files changed, 343 insertions(+), 218 deletions(-) create mode 100644 packages/react-native-renderer/src/ReactFabricPublicInstance.js create mode 100644 packages/react-native-renderer/src/ReactFabricPublicInstanceUtils.js diff --git a/packages/react-native-renderer/src/NativeMethodsMixinUtils.js b/packages/react-native-renderer/src/NativeMethodsMixinUtils.js index b9dc7d43949be..bda6b263e352e 100644 --- a/packages/react-native-renderer/src/NativeMethodsMixinUtils.js +++ b/packages/react-native-renderer/src/NativeMethodsMixinUtils.js @@ -45,26 +45,6 @@ export function mountSafeCallback_NOT_REALLY_SAFE( }; } -export function throwOnStylesProp(component: any, props: any) { - if (props.styles !== undefined) { - const owner = component._owner || null; - const name = component.constructor.displayName; - let msg = - '`styles` is not a supported property of `' + - name + - '`, did ' + - 'you mean `style` (singular)?'; - if (owner && owner.constructor && owner.constructor.displayName) { - msg += - '\n\nCheck the `' + - owner.constructor.displayName + - '` parent ' + - ' component.'; - } - throw new Error(msg); - } -} - export function warnForStyleProps(props: any, validAttributes: any) { if (__DEV__) { for (const key in validAttributes.style) { diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index 8b78bdc8296c6..8051bd74adcc8 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -28,6 +28,7 @@ import { import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal'; import {setBatchingImplementation} from './legacy-events/ReactGenericBatching'; import ReactVersion from 'shared/ReactVersion'; +import {getNativeTagFromPublicInstance} from './ReactFabricPublicInstanceUtils'; // Modules provided by RN: import { @@ -44,6 +45,7 @@ import { import {LegacyRoot, ConcurrentRoot} from 'react-reconciler/src/ReactRootTags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import getComponentNameFromType from 'shared/getComponentNameFromType'; +import type {PublicInstance} from './ReactFabricHostConfig'; const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; @@ -67,19 +69,23 @@ function findHostInstance_DEPRECATED( owner.stateNode._warnedAboutRefsInRender = true; } } + if (componentOrHandle == null) { return null; } - // $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN - if (componentOrHandle._nativeTag) { - // $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN - return componentOrHandle; + + // For compatibility with Paper + if (componentOrHandle._nativeTag != null) { + // $FlowExpectedError[incompatible-cast] For compatibility with Paper (when using Fabric) + return (componentOrHandle: PublicInstance); } - // $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN - if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) { - // $FlowFixMe Flow has hardcoded values for React DOM that don't work with RN - return componentOrHandle.canonical; + + // Fabric-specific + if (componentOrHandle.publicInstance != null) { + // $FlowExpectedError[incompatible-cast] For compatibility with Fabric (when using Paper) + return (componentOrHandle.publicInstance: PublicInstance); } + let hostInstance; if (__DEV__) { hostInstance = findHostInstanceWithWarning( @@ -111,19 +117,28 @@ function findNodeHandle(componentOrHandle: any): ?number { owner.stateNode._warnedAboutRefsInRender = true; } } + if (componentOrHandle == null) { return null; } + if (typeof componentOrHandle === 'number') { // Already a node handle return componentOrHandle; } + + // For compatibility with Paper if (componentOrHandle._nativeTag) { return componentOrHandle._nativeTag; } - if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) { - return componentOrHandle.canonical._nativeTag; + + if (componentOrHandle.internals != null) { + const nativeTag = componentOrHandle.internals.nativeTag; + if (nativeTag != null) { + return nativeTag; + } } + let hostInstance; if (__DEV__) { hostInstance = findHostInstanceWithWarning( @@ -138,7 +153,14 @@ function findNodeHandle(componentOrHandle: any): ?number { return hostInstance; } - return hostInstance._nativeTag; + // $FlowExpectedError[prop-missing] For compatibility with Paper (when using Fabric) + if (hostInstance._nativeTag != null) { + // $FlowExpectedError[incompatible-return] + return hostInstance._nativeTag; + } + + // $FlowExpectedError[incompatible-call] For compatibility with Fabric (when using Paper) + return getNativeTagFromPublicInstance(hostInstance); } function dispatchCommand(handle: any, command: string, args: Array) { @@ -265,6 +287,7 @@ export { }; injectIntoDevTools({ + // $FlowExpectedError[incompatible-call] The type of `Instance` in `getClosestInstanceFromNode` does not match in Fabric and Paper, so it fails to typecheck here. findFiberByHostInstance: getClosestInstanceFromNode, bundleType: __DEV__ ? 1 : 0, version: ReactVersion, diff --git a/packages/react-native-renderer/src/ReactFabricComponentTree.js b/packages/react-native-renderer/src/ReactFabricComponentTree.js index 7db7505bee590..8ecfe33904269 100644 --- a/packages/react-native-renderer/src/ReactFabricComponentTree.js +++ b/packages/react-native-renderer/src/ReactFabricComponentTree.js @@ -3,28 +3,53 @@ * * 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 */ -function getInstanceFromInstance(instanceHandle) { - return instanceHandle; +import type { + PublicInstance, + Instance, + Props, + TextInstance, +} from './ReactFabricHostConfig'; +import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; +import {getPublicInstance} from './ReactFabricHostConfig'; + +// `node` is typed incorrectly here. The proper type should be `PublicInstance`. +// This is ok in DOM because they types are interchangeable, but in React Native +// they aren't. +function getInstanceFromNode(node: Instance | TextInstance): Fiber | null { + const instance: Instance = (node: $FlowFixMe); // In React Native, node is never a text instance + + if ( + instance.internals != null && + instance.internals.internalInstanceHandle != null + ) { + return instance.internals.internalInstanceHandle; + } + + // $FlowFixMe[incompatible-return] DevTools incorrectly passes a fiber in React Native. + return node; } -function getTagFromInstance(inst) { - const nativeInstance = inst.stateNode.canonical; +function getNodeFromInstance(fiber: Fiber): PublicInstance { + const publicInstance = getPublicInstance(fiber.stateNode); - if (!nativeInstance._nativeTag) { - throw new Error('All native instances should have a tag.'); + if (publicInstance == null) { + throw new Error('Could not find host instance from fiber'); } - return nativeInstance; + return publicInstance; +} + +function getFiberCurrentPropsFromNode(instance: Instance): Props { + return instance && instance.internals && instance.internals.currentProps; } export { - getInstanceFromInstance as getClosestInstanceFromNode, - getInstanceFromInstance as getInstanceFromNode, - getTagFromInstance as getNodeFromInstance, + getInstanceFromNode, + getInstanceFromNode as getClosestInstanceFromNode, + getNodeFromInstance, + getFiberCurrentPropsFromNode, }; - -export function getFiberCurrentPropsFromNode(inst) { - return inst.canonical.currentProps; -} diff --git a/packages/react-native-renderer/src/ReactFabricEventEmitter.js b/packages/react-native-renderer/src/ReactFabricEventEmitter.js index b4a4694045d82..cfea7a08b4e24 100644 --- a/packages/react-native-renderer/src/ReactFabricEventEmitter.js +++ b/packages/react-native-renderer/src/ReactFabricEventEmitter.js @@ -25,6 +25,7 @@ import { import {batchedUpdates} from './legacy-events/ReactGenericBatching'; import accumulateInto from './legacy-events/accumulateInto'; +import {getPublicInstance} from './ReactFabricHostConfig'; import getListener from './ReactNativeGetListener'; import {runEventsInBatch} from './legacy-events/EventBatching'; @@ -92,7 +93,8 @@ export function dispatchEvent( const stateNode = targetFiber.stateNode; // Guard against Fiber being unmounted if (stateNode != null) { - eventTarget = stateNode.canonical; + // $FlowExpectedError[incompatible-cast] public instances in Fabric do not implement `EventTarget` yet. + eventTarget = (getPublicInstance(stateNode): EventTarget); } } diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index 30982a7ad0ee0..602e3c310106b 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -7,25 +7,13 @@ * @flow */ -import type {ElementRef} from 'react'; -import type { - HostComponent, - MeasureInWindowOnSuccessCallback, - MeasureLayoutOnSuccessCallback, - MeasureOnSuccessCallback, - NativeMethods, - ViewConfig, - TouchedViewDataAtPoint, -} from './ReactNativeTypes'; - +import type {TouchedViewDataAtPoint, ViewConfig} from './ReactNativeTypes'; import { - mountSafeCallback_NOT_REALLY_SAFE, - warnForStyleProps, -} from './NativeMethodsMixinUtils'; + createPublicInstance, + type ReactFabricHostComponent, +} from './ReactFabricPublicInstance'; import {create, diff} from './ReactNativeAttributePayload'; - import {dispatchEvent} from './ReactFabricEventEmitter'; - import { DefaultEventPriority, DiscreteEventPriority, @@ -34,7 +22,6 @@ import { // Modules provided by RN: import { ReactNativeViewConfigRegistry, - TextInputState, deepFreezeAndThrowOnMutationInDev, } from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; @@ -49,14 +36,9 @@ const { appendChildToSet: appendChildNodeToSet, completeRoot, registerEventHandler, - measure: fabricMeasure, - measureInWindow: fabricMeasureInWindow, - measureLayout: fabricMeasureLayout, unstable_DefaultEventPriority: FabricDefaultPriority, unstable_DiscreteEventPriority: FabricDiscretePriority, unstable_getCurrentEventPriority: fabricGetCurrentEventPriority, - setNativeProps, - getBoundingClientRect: fabricGetBoundingClientRect, } = nativeFabricUIManager; const {get: getViewConfigForType} = ReactNativeViewConfigRegistry; @@ -71,9 +53,18 @@ type Node = Object; export type Type = string; export type Props = Object; export type Instance = { + // Reference to the shadow node. node: Node, - canonical: ReactFabricHostComponent, - ... + // Exposed through refs. + publicInstance: ReactFabricHostComponent, + // We define this as an object instead of as separate fields to simplify + // making copies of instance where `internals` don't change. + internals: { + nativeTag: number, + viewConfig: ViewConfig, + currentProps: Props, + internalInstanceHandle: Object, + }, }; export type TextInstance = {node: Node, ...}; export type HydratableInstance = Instance | TextInstance; @@ -107,114 +98,6 @@ if (registerEventHandler) { registerEventHandler(dispatchEvent); } -/** - * This is used for refs on host components. - */ -class ReactFabricHostComponent implements NativeMethods { - _nativeTag: number; - viewConfig: ViewConfig; - currentProps: Props; - _internalInstanceHandle: Object; - - constructor( - tag: number, - viewConfig: ViewConfig, - props: Props, - internalInstanceHandle: Object, - ) { - this._nativeTag = tag; - this.viewConfig = viewConfig; - this.currentProps = props; - this._internalInstanceHandle = internalInstanceHandle; - } - - blur() { - TextInputState.blurTextInput(this); - } - - focus() { - TextInputState.focusTextInput(this); - } - - measure(callback: MeasureOnSuccessCallback) { - const {stateNode} = this._internalInstanceHandle; - if (stateNode != null) { - fabricMeasure( - stateNode.node, - mountSafeCallback_NOT_REALLY_SAFE(this, callback), - ); - } - } - - measureInWindow(callback: MeasureInWindowOnSuccessCallback) { - const {stateNode} = this._internalInstanceHandle; - if (stateNode != null) { - fabricMeasureInWindow( - stateNode.node, - mountSafeCallback_NOT_REALLY_SAFE(this, 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 = this._internalInstanceHandle.stateNode; - const fromStateNode = - relativeToNativeNode._internalInstanceHandle.stateNode; - - if (toStateNode != null && fromStateNode != null) { - fabricMeasureLayout( - toStateNode.node, - fromStateNode.node, - mountSafeCallback_NOT_REALLY_SAFE(this, onFail), - mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess), - ); - } - } - - unstable_getBoundingClientRect(): DOMRect { - const {stateNode} = this._internalInstanceHandle; - if (stateNode != null) { - const rect = fabricGetBoundingClientRect(stateNode.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: Object) { - if (__DEV__) { - warnForStyleProps(nativeProps, this.viewConfig.validAttributes); - } - const updatePayload = create(nativeProps, this.viewConfig.validAttributes); - - const {stateNode} = this._internalInstanceHandle; - if (stateNode != null && updatePayload != null) { - setNativeProps(stateNode.node, updatePayload); - } - } -} - export * from 'react-reconciler/src/ReactFiberHostConfigWithNoMutation'; export * from 'react-reconciler/src/ReactFiberHostConfigWithNoHydration'; export * from 'react-reconciler/src/ReactFiberHostConfigWithNoScopes'; @@ -260,16 +143,21 @@ export function createInstance( internalInstanceHandle, // internalInstanceHandle ); - const component = new ReactFabricHostComponent( + const component = createPublicInstance( tag, viewConfig, - props, internalInstanceHandle, ); return { node: node, - canonical: component, + publicInstance: component, + internals: { + nativeTag: tag, + viewConfig, + currentProps: props, + internalInstanceHandle, + }, }; } @@ -339,12 +227,15 @@ export function getChildHostContext( } export function getPublicInstance(instance: Instance): null | PublicInstance { - if (instance.canonical) { - return instance.canonical; + if (instance.publicInstance != null) { + return instance.publicInstance; } - // For compatibility with Paper + // For compatibility with Paper, in case Fabric and Paper are used together + // in the same app. + // $FlowExpectedError[prop-missing] if (instance._nativeTag != null) { + // $FlowExpectedError[incompatible-return] return instance; } @@ -363,12 +254,12 @@ export function prepareUpdate( newProps: Props, hostContext: HostContext, ): null | Object { - const viewConfig = instance.canonical.viewConfig; + const viewConfig = instance.internals.viewConfig; const updatePayload = diff(oldProps, newProps, viewConfig.validAttributes); // TODO: If the event handlers have changed, we need to update the current props // in the commit phase but there is no host config hook to do it yet. // So instead we hack it by updating it in the render phase. - instance.canonical.currentProps = newProps; + instance.internals.currentProps = newProps; return updatePayload; } @@ -447,7 +338,8 @@ export function cloneInstance( } return { node: clone, - canonical: instance.canonical, + publicInstance: instance.publicInstance, + internals: instance.internals, }; } @@ -457,7 +349,7 @@ export function cloneHiddenInstance( props: Props, internalInstanceHandle: Object, ): Instance { - const viewConfig = instance.canonical.viewConfig; + const viewConfig = instance.internals.viewConfig; const node = instance.node; const updatePayload = create( {style: {display: 'none'}}, @@ -465,7 +357,8 @@ export function cloneHiddenInstance( ); return { node: cloneNodeWithNewProps(node, updatePayload), - canonical: instance.canonical, + publicInstance: instance.publicInstance, + internals: instance.internals, }; } diff --git a/packages/react-native-renderer/src/ReactFabricPublicInstance.js b/packages/react-native-renderer/src/ReactFabricPublicInstance.js new file mode 100644 index 0000000000000..6cc661ba47151 --- /dev/null +++ b/packages/react-native-renderer/src/ReactFabricPublicInstance.js @@ -0,0 +1,163 @@ +/** + * 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, + NativeMethods, + HostComponent, + MeasureInWindowOnSuccessCallback, + MeasureLayoutOnSuccessCallback, + MeasureOnSuccessCallback, +} from './ReactNativeTypes'; + +import {TextInputState} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; +import {create} from './ReactNativeAttributePayload'; + +import {warnForStyleProps} from './NativeMethodsMixinUtils'; + +const { + measure: fabricMeasure, + measureInWindow: fabricMeasureInWindow, + measureLayout: fabricMeasureLayout, + setNativeProps, + getBoundingClientRect: fabricGetBoundingClientRect, +} = nativeFabricUIManager; + +const noop = () => {}; + +type ParamOf = $Call<((arg: T) => mixed) => T, Fn>; +type ShadowNode = ParamOf; + +/** + * This is used for refs on host components. + */ +export class ReactFabricHostComponent implements NativeMethods { + // The native tag has to be accessible in `ReactFabricHostComponentUtils`. + __nativeTag: number; + + _viewConfig: ViewConfig; + _internalInstanceHandle: mixed; + + 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 = getShadowNodeFromInternalInstanceHandle( + this._internalInstanceHandle, + ); + if (node != null) { + fabricMeasure(node, callback); + } + } + + measureInWindow(callback: MeasureInWindowOnSuccessCallback) { + const node = getShadowNodeFromInternalInstanceHandle( + 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 = getShadowNodeFromInternalInstanceHandle( + this._internalInstanceHandle, + ); + const fromStateNode = getShadowNodeFromInternalInstanceHandle( + relativeToNativeNode._internalInstanceHandle, + ); + + if (toStateNode != null && fromStateNode != null) { + fabricMeasureLayout( + toStateNode, + fromStateNode, + onFail != null ? onFail : noop, + onSuccess != null ? onSuccess : noop, + ); + } + } + + unstable_getBoundingClientRect(): DOMRect { + const node = getShadowNodeFromInternalInstanceHandle( + 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 = getShadowNodeFromInternalInstanceHandle( + this._internalInstanceHandle, + ); + if (node != null && updatePayload != null) { + setNativeProps(node, updatePayload); + } + } +} + +function getShadowNodeFromInternalInstanceHandle( + internalInstanceHandle: mixed, +): ?ShadowNode { + // $FlowExpectedError[incompatible-use] internalInstanceHandle is opaque but we need to make an exception here. + return internalInstanceHandle?.stateNode?.node; +} + +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 new file mode 100644 index 0000000000000..2e4a7abc17040 --- /dev/null +++ b/packages/react-native-renderer/src/ReactFabricPublicInstanceUtils.js @@ -0,0 +1,23 @@ +/** + * 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'; + +/** + * 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; +} diff --git a/packages/react-native-renderer/src/ReactNativeComponentTree.js b/packages/react-native-renderer/src/ReactNativeComponentTree.js index 3af3fa5a443f5..e223b048afba0 100644 --- a/packages/react-native-renderer/src/ReactNativeComponentTree.js +++ b/packages/react-native-renderer/src/ReactNativeComponentTree.js @@ -24,9 +24,10 @@ function getInstanceFromTag(tag) { function getTagFromInstance(inst) { let nativeInstance = inst.stateNode; let tag = nativeInstance._nativeTag; - if (tag === undefined) { - nativeInstance = nativeInstance.canonical; - tag = nativeInstance._nativeTag; + if (tag === undefined && nativeInstance.internals) { + // For compatibility with Fabric + tag = nativeInstance.internals.nativeTag; + nativeInstance = nativeInstance.publicInstance; } if (!tag) { diff --git a/packages/react-native-renderer/src/ReactNativeFiberInspector.js b/packages/react-native-renderer/src/ReactNativeFiberInspector.js index 7641e9314f00f..80dea8a434746 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberInspector.js +++ b/packages/react-native-renderer/src/ReactNativeFiberInspector.js @@ -214,11 +214,12 @@ if (__DEV__) { } closestInstance = - internalInstanceHandle.stateNode.canonical._internalInstanceHandle; + internalInstanceHandle.stateNode.fabricInternals + .internalInstanceHandle; // Note: this is deprecated and we want to remove it ASAP. Keeping it here for React DevTools compatibility for now. const nativeViewTag = - internalInstanceHandle.stateNode.canonical._nativeTag; + internalInstanceHandle.stateNode.fabricInternals.nativeTag; nativeFabricUIManager.measure( internalInstanceHandle.stateNode.node, diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js index a3b58315205f3..330875debaefd 100644 --- a/packages/react-native-renderer/src/ReactNativeHostConfig.js +++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js @@ -218,8 +218,8 @@ export function getChildHostContext( export function getPublicInstance(instance: Instance): * { // $FlowExpectedError[prop-missing] For compatibility with Fabric - if (instance.canonical) { - return instance.canonical; + if (instance.publicInstance != null) { + return instance.publicInstance; } return instance; diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index 6b75be5d07d97..2d0f67971b143 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -47,12 +47,14 @@ import { import {LegacyRoot} from 'react-reconciler/src/ReactRootTags'; import ReactSharedInternals from 'shared/ReactSharedInternals'; import getComponentNameFromType from 'shared/getComponentNameFromType'; +import {getNativeTagFromPublicInstance} from './ReactFabricPublicInstanceUtils'; +import type {PublicInstance} from './ReactNativeHostConfig'; const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; -function findHostInstance_DEPRECATED( - componentOrHandle: any, -): ?React$ElementRef> { +function findHostInstance_DEPRECATED( + componentOrHandle: ?(ElementRef | number), +): ?ElementRef> { if (__DEV__) { const owner = ReactCurrentOwner.current; if (owner !== null && owner.stateNode !== null) { @@ -70,15 +72,23 @@ function findHostInstance_DEPRECATED( owner.stateNode._warnedAboutRefsInRender = true; } } + if (componentOrHandle == null) { return null; } - if (componentOrHandle._nativeTag) { - return componentOrHandle; + + // For compatibility with Paper + if (componentOrHandle._nativeTag != null) { + // $FlowExpectedError[incompatible-cast] For compatibility with Paper (when using Fabric) + return (componentOrHandle: PublicInstance); } - if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) { - return componentOrHandle.canonical; + + // Fabric-specific + if (componentOrHandle.publicInstance != null) { + // $FlowExpectedError[incompatible-cast] For compatibility with Fabric (when using Paper) + return (componentOrHandle.publicInstance: PublicInstance); } + let hostInstance; if (__DEV__) { hostInstance = findHostInstanceWithWarning( @@ -110,19 +120,28 @@ function findNodeHandle(componentOrHandle: any): ?number { owner.stateNode._warnedAboutRefsInRender = true; } } + if (componentOrHandle == null) { return null; } + if (typeof componentOrHandle === 'number') { // Already a node handle return componentOrHandle; } + + // For compatibility with Paper if (componentOrHandle._nativeTag) { return componentOrHandle._nativeTag; } - if (componentOrHandle.canonical && componentOrHandle.canonical._nativeTag) { - return componentOrHandle.canonical._nativeTag; + + if (componentOrHandle.internals != null) { + const nativeTag = componentOrHandle.internals.nativeTag; + if (nativeTag != null) { + return nativeTag; + } } + let hostInstance; if (__DEV__) { hostInstance = findHostInstanceWithWarning( @@ -137,7 +156,14 @@ function findNodeHandle(componentOrHandle: any): ?number { return hostInstance; } - return hostInstance._nativeTag; + // $FlowExpectedError[prop-missing] For compatibility with Paper (when using Fabric) + if (hostInstance._nativeTag != null) { + // $FlowExpectedError[incompatible-return] + return hostInstance._nativeTag; + } + + // $FlowExpectedError[incompatible-call] For compatibility with Fabric (when using Paper) + return getNativeTagFromPublicInstance(hostInstance); } function dispatchCommand(handle: any, command: string, args: Array) { diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index a6dc47b066155..db69a469c5123 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -218,18 +218,6 @@ export type ReactFabricType = { ... }; -export type ReactNativeEventTarget = { - node: {...}, - canonical: { - _nativeTag: number, - viewConfig: ViewConfig, - currentProps: {...}, - _internalInstanceHandle: {...}, - ... - }, - ... -}; - export type ReactFabricEventTouch = { identifier: number, locationX: number,