From 56922cf751fab6c7ab4c12ddbbd15839959fa255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 16 Apr 2026 18:34:03 +0100 Subject: [PATCH] [react-native-renderer] Delete Paper (legacy) renderer (#36285) ## Summary The Paper renderer is no longer used in React Native. This commit removes all remaining Paper source code, tests, build system references, and Paper backward-compatibility branches in shared code. Deleted Paper-only source files: - ReactNativeRenderer.js, ReactNativeInjection.js, ReactFiberConfigNative.js - ReactNativeComponentTree.js, ReactNativeEventEmitter.js - ReactNativeFiberHostComponent.js, ReactNativeGlobalResponderHandler.js - ReactNativeAttributePayload.js, NativeMethodsMixinUtils.js - ReactFiberConfig.native.js (reconciler fork) - index.js (Paper entry point) Cleaned up shared files: - ReactNativePublicCompat.js: removed _nativeTag checks, UIManager/ legacySendAccessibilityEvent Paper fallbacks - ReactNativeFiberInspector.js: removed getInspectorDataForViewTag, UIManager.measure fallback, Paper branch in getInspectorDataForViewAtPoint - ReactFiberConfigFabric.js: removed _nativeTag backward compat in getPublicInstance, removed getInspectorDataForViewTag from devtools config - ReactNativeTypes.js: removed ReactNativeType (Paper API type) Cleaned up build system: - inlinedHostConfigs.js: removed shortName 'native' config - forks.js: removed dead 'react-native-renderer' case - Deleted ReactNative.js shim and Paper-only test mocks ## How did you test this change? Manually synced the renderer to RN and passed all Fantom tests. Manually verified the differences in the generated `ReactFabric-dev.js` file. Only Paper compat logic has been removed.
diff ```diff --- /tmp/react-fabric-baseline/ReactFabric-dev.js 2026-04-16 16:42:42 +++ build/react-native/implementations/ReactFabric-dev.js 2026-04-16 18:08:43 @@ -30,43 +30,19 @@ : emptyObject; } function createHierarchy(fiberHierarchy) { - return fiberHierarchy.map(function (fiber$jscomp$0) { + return fiberHierarchy.map(function (fiber) { return { - name: getComponentNameFromType(fiber$jscomp$0.type), + name: getComponentNameFromType(fiber.type), getInspectorData: function () { return { - props: getHostProps(fiber$jscomp$0), + props: getHostProps(fiber), measure: function (callback) { - var hostFiber = findCurrentHostFiber(fiber$jscomp$0); - if ( - (hostFiber = - null != hostFiber && - null !== hostFiber.stateNode && - hostFiber.stateNode.node) - ) + var hostFiber = findCurrentHostFiber(fiber); + (hostFiber = + null != hostFiber && + null !== hostFiber.stateNode && + hostFiber.stateNode.node) && nativeFabricUIManager.measure(hostFiber, callback); - else { - hostFiber = ReactNativePrivateInterface.UIManager; - var JSCompiler_temp_const = hostFiber.measure, - JSCompiler_inline_result; - a: { - for (var fiber = fiber$jscomp$0; fiber; ) { - null !== fiber.stateNode && - 5 === fiber.tag && - (JSCompiler_inline_result = findNodeHandle( - fiber.stateNode - )); - if (JSCompiler_inline_result) break a; - fiber = fiber.child; - } - JSCompiler_inline_result = null; - } - return JSCompiler_temp_const.call( - hostFiber, - JSCompiler_inline_result, - callback - ); - } } }; } @@ -1805,18 +1781,6 @@ } return null; } - function doesFiberContain(parentFiber, childFiber) { - for ( - var parentFiberAlternate = parentFiber.alternate; - null !== childFiber; - - ) { - if (childFiber === parentFiber || childFiber === parentFiberAlternate) - return !0; - childFiber = childFiber.return; - } - return !1; - } function traverseVisibleHostChildren( child, searchWithinHosts, @@ -16986,44 +16950,6 @@ function getCurrentFiberForDevTools() { return current; } - function findNodeHandle(componentOrHandle) { - var owner = current; - null !== owner && - isRendering && - null !== owner.stateNode && - (owner.stateNode._warnedAboutRefsInRender || - console.error( - "%s is accessing findNodeHandle inside its render(). render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.", - getComponentNameFromType(owner.type) || "A component" - ), - (owner.stateNode._warnedAboutRefsInRender = !0)); - if (null == componentOrHandle) return null; - if ("number" === typeof componentOrHandle) return componentOrHandle; - if (componentOrHandle._nativeTag) return componentOrHandle._nativeTag; - if ( - null != componentOrHandle.canonical && - null != componentOrHandle.canonical.nativeTag - ) - return componentOrHandle.canonical.nativeTag; - if ( - (owner = - ReactNativePrivateInterface.getNativeTagFromPublicInstance( - componentOrHandle - )) - ) - return owner; - componentOrHandle = findHostInstanceWithWarning( - componentOrHandle, - "findNodeHandle" - ); - return null == componentOrHandle - ? componentOrHandle - : null != componentOrHandle._nativeTag - ? componentOrHandle._nativeTag - : ReactNativePrivateInterface.getNativeTagFromPublicInstance( - componentOrHandle - ); - } function getNodeFromInternalInstanceHandle(internalInstanceHandle) { return ( internalInstanceHandle && @@ -17134,12 +17060,9 @@ } return instance.canonical.publicInstance; } - return null != instance.containerInfo && - null != instance.containerInfo.publicInstance + return null != instance.containerInfo ? instance.containerInfo.publicInstance - : null != instance._nativeTag - ? instance - : null; + : null; } function getPublicInstanceFromHostFiber(fiber) { fiber = getPublicInstance(fiber.stateNode); @@ -18017,7 +17940,6 @@ DefaultEventPriority = 32, IdleEventPriority = 268435456, searchTarget = null, - instanceCache = new Map(), bind = Function.prototype.bind, valueStack = []; var fiberStack = []; @@ -20041,24 +19963,19 @@ _nativeFabricUIManage.unstable_getCurrentEventPriority, extraDevToolsConfig = { getInspectorDataForInstance: getInspectorDataForInstance, - getInspectorDataForViewTag: function (viewTag) { - viewTag = instanceCache.get(viewTag) || null; - return getInspectorDataForInstance(viewTag); - }, getInspectorDataForViewAtPoint: function ( inspectedView, locationX, locationY, callback ) { - var closestInstance = null, - fabricNode = - ReactNativePrivateInterface.getNodeFromPublicInstance( - inspectedView - ); - fabricNode + var closestInstance = null; + (inspectedView = + ReactNativePrivateInterface.getNodeFromPublicInstance( + inspectedView + )) ? nativeFabricUIManager.findNodeAtPoint( - fabricNode, + inspectedView, locationX, locationY, function (internalInstanceHandle) { @@ -20109,32 +20026,9 @@ } } ) - : null != inspectedView._internalFiberInstanceHandleDEV - ? ReactNativePrivateInterface.UIManager.findSubviewIn( - findNodeHandle(inspectedView), - [locationX, locationY], - function (nativeViewTag, left, top, width, height) { - var inspectorData = getInspectorDataForInstance( - instanceCache.get(nativeViewTag) || null - ); - callback( - assign({}, inspectorData, { - pointerY: locationY, - frame: { - left: left, - top: top, - width: width, - height: height - }, - touchedViewTag: nativeViewTag, - closestPublicInstance: nativeViewTag - }) - ); - } - ) - : console.error( - "getInspectorDataForViewAtPoint expects to receive a host component" - ); + : console.error( + "getInspectorDataForViewAtPoint expects to receive a host component" + ); } }, getViewConfigForType = @@ -20368,23 +20262,12 @@ ); }; exports.dispatchCommand = function (handle, command, args) { - var nativeTag = - null != handle._nativeTag - ? handle._nativeTag - : ReactNativePrivateInterface.getNativeTagFromPublicInstance(handle); - null == nativeTag - ? console.error( + handle = ReactNativePrivateInterface.getNodeFromPublicInstance(handle); + null != handle + ? nativeFabricUIManager.dispatchCommand(handle, command, args) + : console.error( "dispatchCommand was called with a ref that isn't a native component. Use React.forwardRef to get access to the underlying native component" - ) - : ((handle = - ReactNativePrivateInterface.getNodeFromPublicInstance(handle)), - null != handle - ? nativeFabricUIManager.dispatchCommand(handle, command, args) - : ReactNativePrivateInterface.UIManager.dispatchViewManagerCommand( - nativeTag, - command, - args - )); + ); }; exports.findHostInstance_DEPRECATED = function (componentOrHandle) { var owner = current; @@ -20402,14 +20285,46 @@ : componentOrHandle.canonical && componentOrHandle.canonical.publicInstance ? componentOrHandle.canonical.publicInstance - : componentOrHandle._nativeTag - ? componentOrHandle - : findHostInstanceWithWarning( - componentOrHandle, - "findHostInstance_DEPRECATED" - ); + : findHostInstanceWithWarning( + componentOrHandle, + "findHostInstance_DEPRECATED" + ); }; - exports.findNodeHandle = findNodeHandle; + exports.findNodeHandle = function (componentOrHandle) { + var owner = current; + null !== owner && + isRendering && + null !== owner.stateNode && + (owner.stateNode._warnedAboutRefsInRender || + console.error( + "%s is accessing findNodeHandle inside its render(). render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.", + getComponentNameFromType(owner.type) || "A component" + ), + (owner.stateNode._warnedAboutRefsInRender = !0)); + if (null == componentOrHandle) return null; + if ("number" === typeof componentOrHandle) return componentOrHandle; + if ( + null != componentOrHandle.canonical && + null != componentOrHandle.canonical.nativeTag + ) + return componentOrHandle.canonical.nativeTag; + if ( + (owner = + ReactNativePrivateInterface.getNativeTagFromPublicInstance( + componentOrHandle + )) + ) + return owner; + componentOrHandle = findHostInstanceWithWarning( + componentOrHandle, + "findNodeHandle" + ); + return null == componentOrHandle + ? componentOrHandle + : ReactNativePrivateInterface.getNativeTagFromPublicInstance( + componentOrHandle + ); + }; exports.getNodeFromInternalInstanceHandle = getNodeFromInternalInstanceHandle; exports.getPublicInstanceFromInternalInstanceHandle = function ( @@ -20433,14 +20348,6 @@ : null; }; exports.isChildPublicInstance = function (parentInstance, childInstance) { - if ( - parentInstance._internalFiberInstanceHandleDEV && - childInstance._internalFiberInstanceHandleDEV - ) - return doesFiberContain( - parentInstance._internalFiberInstanceHandleDEV, - childInstance._internalFiberInstanceHandleDEV - ); parentInstance = ReactNativePrivateInterface.getInternalInstanceHandleFromPublicInstance( parentInstance @@ -20449,9 +20356,27 @@ ReactNativePrivateInterface.getInternalInstanceHandleFromPublicInstance( childInstance ); - return null != parentInstance && null != childInstance - ? doesFiberContain(parentInstance, childInstance) - : !1; + if (null != parentInstance && null != childInstance) { + a: { + for ( + var parentFiberAlternate = parentInstance.alternate; + null !== childInstance; + + ) { + if ( + childInstance === parentInstance || + childInstance === parentFiberAlternate + ) { + parentInstance = !0; + break a; + } + childInstance = childInstance.return; + } + parentInstance = !1; + } + return parentInstance; + } + return !1; }; exports.render = function ( element, @@ -20521,22 +20446,12 @@ return element; }; exports.sendAccessibilityEvent = function (handle, eventType) { - var nativeTag = - null != handle._nativeTag - ? handle._nativeTag - : ReactNativePrivateInterface.getNativeTagFromPublicInstance(handle); - null == nativeTag - ? console.error( + handle = ReactNativePrivateInterface.getNodeFromPublicInstance(handle); + null != handle + ? nativeFabricUIManager.sendAccessibilityEvent(handle, eventType) + : console.error( "sendAccessibilityEvent was called with a ref that isn't a native component. Use React.forwardRef to get access to the underlying native component" - ) - : ((handle = - ReactNativePrivateInterface.getNodeFromPublicInstance(handle)), - null != handle - ? nativeFabricUIManager.sendAccessibilityEvent(handle, eventType) - : ReactNativePrivateInterface.legacySendAccessibilityEvent( - nativeTag, - eventType - )); + ); }; exports.stopSurface = function (containerTag) { var root = roots.get(containerTag); ```
--- packages/react-native-renderer/index.js | 16 - .../src/NativeMethodsMixinUtils.js | 64 -- .../src/ReactFiberConfigFabric.js | 16 +- .../src/ReactFiberConfigNative.js | 845 ------------------ .../src/ReactNativeAttributePayload.js | 492 ---------- .../src/ReactNativeComponentTree.js | 52 -- .../src/ReactNativeEventEmitter.js | 243 ----- .../src/ReactNativeFiberHostComponent.js | 128 --- .../src/ReactNativeFiberInspector.js | 66 +- .../src/ReactNativeGlobalResponderHandler.js | 24 - .../src/ReactNativeInjection.js | 41 - .../src/ReactNativePublicCompat.js | 72 +- .../src/ReactNativeRenderer.js | 223 ----- .../src/ReactNativeTypes.js | 30 - .../Libraries/ReactPrivate/BatchedBridge.js | 14 - .../ReactPrivate/ExceptionsManager.js | 12 - .../Libraries/ReactPrivate/Platform.js | 11 - .../Libraries/ReactPrivate/RCTEventEmitter.js | 14 - .../ReactNativePrivateInterface.js | 24 - .../Libraries/ReactPrivate/TextInputState.js | 18 - .../Libraries/ReactPrivate/UIManager.js | 194 ---- .../Libraries/ReactPrivate/flattenStyle.js | 14 - .../legacySendAccessibilityEvent.js | 10 - .../__tests__/ReactFabric-test.internal.js | 4 + .../ReactFabricAndNative-test.internal.js | 231 ----- ...actNativeAttributePayload-test.internal.js | 287 ------ .../ReactNativeEvents-test.internal.js | 554 ------------ .../ReactNativeMount-test.internal.js | 771 ---------------- ...ReactNativeComponentClass-test.internal.js | 76 -- .../src/forks/ReactFiberConfig.native.js | 11 - .../__tests__/ReactMismatchedVersions-test.js | 10 - scripts/flow/react-native-host-hooks.js | 103 --- scripts/rollup/forks.js | 15 - .../rollup/shims/react-native/ReactNative.js | 24 - scripts/shared/inlinedHostConfigs.js | 10 - 35 files changed, 16 insertions(+), 4703 deletions(-) delete mode 100644 packages/react-native-renderer/index.js delete mode 100644 packages/react-native-renderer/src/NativeMethodsMixinUtils.js delete mode 100644 packages/react-native-renderer/src/ReactFiberConfigNative.js delete mode 100644 packages/react-native-renderer/src/ReactNativeAttributePayload.js delete mode 100644 packages/react-native-renderer/src/ReactNativeComponentTree.js delete mode 100644 packages/react-native-renderer/src/ReactNativeEventEmitter.js delete mode 100644 packages/react-native-renderer/src/ReactNativeFiberHostComponent.js delete mode 100644 packages/react-native-renderer/src/ReactNativeGlobalResponderHandler.js delete mode 100644 packages/react-native-renderer/src/ReactNativeInjection.js delete mode 100644 packages/react-native-renderer/src/ReactNativeRenderer.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/BatchedBridge.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ExceptionsManager.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/Platform.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RCTEventEmitter.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/TextInputState.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/UIManager.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/flattenStyle.js delete mode 100644 packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/legacySendAccessibilityEvent.js delete mode 100644 packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js delete mode 100644 packages/react-native-renderer/src/__tests__/ReactNativeAttributePayload-test.internal.js delete mode 100644 packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js delete mode 100644 packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js delete mode 100644 packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js delete mode 100644 packages/react-reconciler/src/forks/ReactFiberConfig.native.js delete mode 100644 scripts/rollup/shims/react-native/ReactNative.js diff --git a/packages/react-native-renderer/index.js b/packages/react-native-renderer/index.js deleted file mode 100644 index 131c066ff44b..000000000000 --- a/packages/react-native-renderer/index.js +++ /dev/null @@ -1,16 +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 - */ - -import type {ReactNativeType} from './src/ReactNativeTypes'; -import * as ReactNative from './src/ReactNativeRenderer'; -// Assert that the exports line up with the type we're going to expose. -(ReactNative: ReactNativeType); - -// TODO: Delete the legacy renderer, only Fabric is used now. -export * from './src/ReactNativeRenderer'; diff --git a/packages/react-native-renderer/src/NativeMethodsMixinUtils.js b/packages/react-native-renderer/src/NativeMethodsMixinUtils.js deleted file mode 100644 index bda6b263e352..000000000000 --- a/packages/react-native-renderer/src/NativeMethodsMixinUtils.js +++ /dev/null @@ -1,64 +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 - */ - -/** - * In the future, we should cleanup callbacks by cancelling them instead of - * using this. - */ -export function mountSafeCallback_NOT_REALLY_SAFE( - context: any, - callback: ?Function, -): any { - return function () { - if (!callback) { - return undefined; - } - // This protects against createClass() components. - // We don't know if there is code depending on it. - // We intentionally don't use isMounted() because even accessing - // isMounted property on a React ES6 class will trigger a warning. - if (typeof context.__isMounted === 'boolean') { - if (!context.__isMounted) { - return undefined; - } - } - - // FIXME: there used to be other branches that protected - // against unmounted host components. But RN host components don't - // define isMounted() anymore, so those checks didn't do anything. - - // They caused false positive warning noise so we removed them: - // https://github.com/facebook/react-native/issues/18868#issuecomment-413579095 - - // However, this means that the callback is NOT guaranteed to be safe - // for host components. The solution we should implement is to make - // UIManager.measure() and similar calls truly cancelable. Then we - // can change our own code calling them to cancel when something unmounts. - - return callback.apply(context, arguments); - }; -} - -export function warnForStyleProps(props: any, validAttributes: any) { - 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-renderer/src/ReactFiberConfigFabric.js b/packages/react-native-renderer/src/ReactFiberConfigFabric.js index 533b20fa6d74..7b603201cc5d 100644 --- a/packages/react-native-renderer/src/ReactFiberConfigFabric.js +++ b/packages/react-native-renderer/src/ReactFiberConfigFabric.js @@ -66,7 +66,6 @@ import {getClosestInstanceFromNode} from './ReactFabricComponentTree'; import {compareDocumentPositionForEmptyFragment} from 'shared/ReactDOMFragmentRefShared'; import { - getInspectorDataForViewTag, getInspectorDataForViewAtPoint, getInspectorDataForInstance, } from './ReactNativeFiberInspector'; @@ -79,7 +78,6 @@ export {default as rendererVersion} from 'shared/ReactVersion'; // TODO: Conside export const rendererPackageName = 'react-native-renderer'; export const extraDevToolsConfig = { getInspectorDataForInstance, - getInspectorDataForViewTag, getInspectorDataForViewAtPoint, }; @@ -142,8 +140,6 @@ export type TransitionStatus = mixed; export type RendererInspectionConfig = $ReadOnly<{ getInspectorDataForInstance?: (instance: Fiber | null) => InspectorData, - // Deprecated. Replaced with getInspectorDataForViewAtPoint. - getInspectorDataForViewTag?: (tag: number) => Object, getInspectorDataForViewAtPoint?: ( inspectedView: Object, locationX: number, @@ -313,17 +309,7 @@ export function getPublicInstance(instance: Instance): null | PublicInstance { // Handle root containers if (instance.containerInfo != null) { - if (instance.containerInfo.publicInstance != null) { - return instance.containerInfo.publicInstance; - } - } - - // For compatibility with the legacy renderer, in case it's used with Fabric - // in the same app. - // $FlowExpectedError[prop-missing] - if (instance._nativeTag != null) { - // $FlowExpectedError[incompatible-return] - return instance; + return instance.containerInfo.publicInstance; } return null; diff --git a/packages/react-native-renderer/src/ReactFiberConfigNative.js b/packages/react-native-renderer/src/ReactFiberConfigNative.js deleted file mode 100644 index 404ae7a54a85..000000000000 --- a/packages/react-native-renderer/src/ReactFiberConfigNative.js +++ /dev/null @@ -1,845 +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 - */ - -import type {InspectorData, TouchedViewDataAtPoint} from './ReactNativeTypes'; -import type {TransitionTypes} from 'react/src/ReactTransitionType'; - -// Modules provided by RN: -import { - ReactNativeViewConfigRegistry, - UIManager, - deepFreezeAndThrowOnMutationInDev, - createPublicInstance, - type PublicRootInstance, -} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - -import {create, diff} from './ReactNativeAttributePayload'; -import { - precacheFiberNode, - uncacheFiberNode, - updateFiberProps, - getClosestInstanceFromNode, -} from './ReactNativeComponentTree'; -import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent'; - -import { - DefaultEventPriority, - NoEventPriority, - type EventPriority, -} from 'react-reconciler/src/ReactEventPriorities'; -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; - -import {enableProfilerTimer} from 'shared/ReactFeatureFlags'; - -import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; -import type {ReactContext} from 'shared/ReactTypes'; - -import { - getInspectorDataForViewTag, - getInspectorDataForViewAtPoint, - getInspectorDataForInstance, -} from './ReactNativeFiberInspector'; - -export {default as rendererVersion} from 'shared/ReactVersion'; // TODO: Consider exporting the react-native version. -export const rendererPackageName = 'react-native-renderer'; -export const extraDevToolsConfig = { - getInspectorDataForInstance, - getInspectorDataForViewTag, - getInspectorDataForViewAtPoint, -}; - -const {get: getViewConfigForType} = ReactNativeViewConfigRegistry; - -export type Type = string; -export type Props = Object; -export type Container = { - containerTag: number, - publicInstance: PublicRootInstance | null, -}; -export type Instance = ReactNativeFiberHostComponent; -export type TextInstance = number; -export type HydratableInstance = Instance | TextInstance; -export type PublicInstance = Instance; -export type HostContext = $ReadOnly<{ - isInAParentText: boolean, -}>; -export type UpdatePayload = Object; // Unused -export type ChildSet = void; // Unused - -export type TimeoutHandle = TimeoutID; -export type NoTimeout = -1; -export type TransitionStatus = mixed; - -export type RendererInspectionConfig = $ReadOnly<{ - getInspectorDataForInstance?: (instance: Fiber | null) => InspectorData, - // Deprecated. Replaced with getInspectorDataForViewAtPoint. - getInspectorDataForViewTag?: (tag: number) => Object, - getInspectorDataForViewAtPoint?: ( - inspectedView: Object, - locationX: number, - locationY: number, - callback: (viewData: TouchedViewDataAtPoint) => mixed, - ) => void, -}>; - -// Counter for uniquely identifying views. -// % 10 === 1 means it is a rootTag. -// % 2 === 0 means it is a Fabric tag. -let nextReactTag = 3; -function allocateTag() { - let tag = nextReactTag; - if (tag % 10 === 1) { - tag += 2; - } - nextReactTag = tag + 2; - return tag; -} - -function recursivelyUncacheFiberNode(node: Instance | TextInstance) { - if (typeof node === 'number') { - // Leaf node (eg text) - uncacheFiberNode(node); - } else { - uncacheFiberNode((node: any)._nativeTag); - - (node: any)._children.forEach(recursivelyUncacheFiberNode); - } -} - -export * from 'react-reconciler/src/ReactFiberConfigWithNoPersistence'; -export * from 'react-reconciler/src/ReactFiberConfigWithNoHydration'; -export * from 'react-reconciler/src/ReactFiberConfigWithNoScopes'; -export * from 'react-reconciler/src/ReactFiberConfigWithNoTestSelectors'; -export * from 'react-reconciler/src/ReactFiberConfigWithNoMicrotasks'; -export * from 'react-reconciler/src/ReactFiberConfigWithNoResources'; -export * from 'react-reconciler/src/ReactFiberConfigWithNoSingletons'; - -export function appendInitialChild( - parentInstance: Instance, - child: Instance | TextInstance, -): void { - parentInstance._children.push(child); -} - -export function createInstance( - type: string, - props: Props, - rootContainerInstance: Container, - hostContext: HostContext, - internalInstanceHandle: Object, -): Instance { - const tag = allocateTag(); - const viewConfig = getViewConfigForType(type); - - if (__DEV__) { - for (const key in viewConfig.validAttributes) { - if (props.hasOwnProperty(key)) { - deepFreezeAndThrowOnMutationInDev(props[key]); - } - } - } - - const updatePayload = create(props, viewConfig.validAttributes); - - UIManager.createView( - tag, // reactTag - viewConfig.uiViewClassName, // viewName - rootContainerInstance.containerTag, // rootTag - updatePayload, // props - ); - - const component = new ReactNativeFiberHostComponent( - tag, - viewConfig, - internalInstanceHandle, - ); - - precacheFiberNode(internalInstanceHandle, tag); - updateFiberProps(tag, props); - - // Not sure how to avoid this cast. Flow is okay if the component is defined - // in the same file but if it's external it can't see the types. - return ((component: any): Instance); -} - -export function cloneMutableInstance( - instance: Instance, - keepChildren: boolean, -): Instance { - throw new Error('Not yet implemented.'); -} - -export function createTextInstance( - text: string, - rootContainerInstance: Container, - hostContext: HostContext, - internalInstanceHandle: Object, -): TextInstance { - if (!hostContext.isInAParentText) { - throw new Error('Text strings must be rendered within a component.'); - } - - const tag = allocateTag(); - - UIManager.createView( - tag, // reactTag - 'RCTRawText', // viewName - rootContainerInstance.containerTag, // rootTag - {text: text}, // props - ); - - precacheFiberNode(internalInstanceHandle, tag); - - return tag; -} - -export function cloneMutableTextInstance( - textInstance: TextInstance, -): TextInstance { - throw new Error('Not yet implemented.'); -} - -export type FragmentInstanceType = null; - -export function createFragmentInstance( - fragmentFiber: Fiber, -): FragmentInstanceType { - return null; -} - -export function updateFragmentInstanceFiber( - fragmentFiber: Fiber, - instance: FragmentInstanceType, -): void { - // Noop -} - -export function commitNewChildToFragmentInstance( - child: PublicInstance, - fragmentInstance: FragmentInstanceType, -): void { - // Noop -} - -export function deleteChildFromFragmentInstance( - child: PublicInstance, - fragmentInstance: FragmentInstanceType, -): void { - // Noop -} - -export function finalizeInitialChildren( - parentInstance: Instance, - type: string, - props: Props, - hostContext: HostContext, -): boolean { - // Don't send a no-op message over the bridge. - if (parentInstance._children.length === 0) { - return false; - } - - // Map from child objects to native tags. - // Either way we need to pass a copy of the Array to prevent it from being frozen. - const nativeTags = parentInstance._children.map(child => - typeof child === 'number' - ? child // Leaf node (eg text) - : child._nativeTag, - ); - - UIManager.setChildren( - parentInstance._nativeTag, // containerTag - nativeTags, // reactTags - ); - - return false; -} - -export function getRootHostContext( - rootContainerInstance: Container, -): HostContext { - return {isInAParentText: false}; -} - -export function getChildHostContext( - parentHostContext: HostContext, - type: string, -): HostContext { - const prevIsInAParentText = parentHostContext.isInAParentText; - const isInAParentText = - type === 'AndroidTextInput' || // Android - type === 'RCTMultilineTextInputView' || // iOS - type === 'RCTSelectableText' || - type === 'RCTSinglelineTextInputView' || // iOS - type === 'RCTText' || - type === 'RCTVirtualText'; - - if (prevIsInAParentText !== isInAParentText) { - return {isInAParentText}; - } else { - return parentHostContext; - } -} - -export function getPublicInstance(instance: Instance): PublicInstance { - // $FlowExpectedError[prop-missing] For compatibility with Fabric - if (instance.canonical != null) { - if (instance.canonical.publicInstance == null) { - // $FlowExpectedError[incompatible-use] - instance.canonical.publicInstance = createPublicInstance( - // $FlowExpectedError[incompatible-use] - instance.canonical.nativeTag, - // $FlowExpectedError[incompatible-use] - instance.canonical.viewConfig, - // $FlowExpectedError[incompatible-use] - instance.canonical.internalInstanceHandle, - // $FlowExpectedError[incompatible-use] - instance.canonical.publicRootInstance ?? null, - ); - // This was only necessary to create the public instance. - // $FlowExpectedError[prop-missing] - instance.canonical.publicRootInstance = null; - } - - // $FlowExpectedError[prop-missing] - // $FlowExpectedError[incompatible-return] - return instance.canonical.publicInstance; - } - - return instance; -} - -export function prepareForCommit(containerInfo: Container): null | Object { - // Noop - return null; -} - -export function resetAfterCommit(containerInfo: Container): void { - // Noop -} - -export const isPrimaryRenderer = true; -export const warnsIfNotActing = true; - -export const scheduleTimeout = setTimeout; -export const cancelTimeout = clearTimeout; -export const noTimeout: -1 = -1; - -export function shouldSetTextContent(type: string, props: Props): boolean { - // TODO (bvaughn) Revisit this decision. - // Always returning false simplifies the createInstance() implementation, - // But creates an additional child Fiber for raw text children. - // No additional native views are created though. - // It's not clear to me which is better so I'm deferring for now. - // More context @ github.com/facebook/react/pull/8560#discussion_r92111303 - return false; -} - -let currentUpdatePriority: EventPriority = NoEventPriority; -export function setCurrentUpdatePriority(newPriority: EventPriority): void { - currentUpdatePriority = newPriority; -} - -export function getCurrentUpdatePriority(): EventPriority { - return currentUpdatePriority; -} - -export function resolveUpdatePriority(): EventPriority { - if (currentUpdatePriority !== NoEventPriority) { - return currentUpdatePriority; - } - return DefaultEventPriority; -} - -export function trackSchedulerEvent(): void {} - -export function resolveEventType(): null | string { - return null; -} - -export function resolveEventTimeStamp(): number { - return -1.1; -} - -export function shouldAttemptEagerTransition(): boolean { - return false; -} - -// ------------------- -// Mutation -// ------------------- - -export const supportsMutation = true; - -export function appendChild( - parentInstance: Instance, - child: Instance | TextInstance, -): void { - const childTag = typeof child === 'number' ? child : child._nativeTag; - const children = parentInstance._children; - const index = children.indexOf(child); - - if (index >= 0) { - children.splice(index, 1); - children.push(child); - - UIManager.manageChildren( - parentInstance._nativeTag, // containerTag - [index], // moveFromIndices - [children.length - 1], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [], // removeAtIndices - ); - } else { - children.push(child); - - UIManager.manageChildren( - parentInstance._nativeTag, // containerTag - [], // moveFromIndices - [], // moveToIndices - [childTag], // addChildReactTags - [children.length - 1], // addAtIndices - [], // removeAtIndices - ); - } -} - -export function appendChildToContainer( - parentInstance: Container, - child: Instance | TextInstance, -): void { - const childTag = typeof child === 'number' ? child : child._nativeTag; - UIManager.setChildren( - parentInstance.containerTag, // containerTag - [childTag], // reactTags - ); -} - -export function commitTextUpdate( - textInstance: TextInstance, - oldText: string, - newText: string, -): void { - UIManager.updateView( - textInstance, // reactTag - 'RCTRawText', // viewName - {text: newText}, // props - ); -} - -export function commitMount( - instance: Instance, - type: string, - newProps: Props, - internalInstanceHandle: Object, -): void { - // Noop -} - -export function commitUpdate( - instance: Instance, - type: string, - oldProps: Props, - newProps: Props, - internalInstanceHandle: Object, -): void { - const viewConfig = instance.viewConfig; - - updateFiberProps(instance._nativeTag, newProps); - - const updatePayload = diff(oldProps, newProps, viewConfig.validAttributes); - - // Avoid the overhead of bridge calls if there's no update. - // This is an expensive no-op for Android, and causes an unnecessary - // view invalidation for certain components (eg RCTTextInput) on iOS. - if (updatePayload != null) { - UIManager.updateView( - instance._nativeTag, // reactTag - viewConfig.uiViewClassName, // viewName - updatePayload, // props - ); - } -} - -export function insertBefore( - parentInstance: Instance, - child: Instance | TextInstance, - beforeChild: Instance | TextInstance, -): void { - const children = (parentInstance: any)._children; - const index = children.indexOf(child); - - // Move existing child or add new child? - if (index >= 0) { - children.splice(index, 1); - const beforeChildIndex = children.indexOf(beforeChild); - children.splice(beforeChildIndex, 0, child); - - UIManager.manageChildren( - (parentInstance: any)._nativeTag, // containerID - [index], // moveFromIndices - [beforeChildIndex], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [], // removeAtIndices - ); - } else { - const beforeChildIndex = children.indexOf(beforeChild); - children.splice(beforeChildIndex, 0, child); - - const childTag = typeof child === 'number' ? child : child._nativeTag; - - UIManager.manageChildren( - (parentInstance: any)._nativeTag, // containerID - [], // moveFromIndices - [], // moveToIndices - [childTag], // addChildReactTags - [beforeChildIndex], // addAtIndices - [], // removeAtIndices - ); - } -} - -export function insertInContainerBefore( - parentInstance: Container, - child: Instance | TextInstance, - beforeChild: Instance | TextInstance, -): void { - // TODO (bvaughn): Remove this check when... - // We create a wrapper object for the container in ReactNative render() - // Or we refactor to remove wrapper objects entirely. - // For more info on pros/cons see PR #8560 description. - if (typeof parentInstance === 'number') { - throw new Error('Container does not support insertBefore operation'); - } -} - -export function removeChild( - parentInstance: Instance, - child: Instance | TextInstance, -): void { - recursivelyUncacheFiberNode(child); - const children = parentInstance._children; - const index = children.indexOf(child); - - children.splice(index, 1); - - UIManager.manageChildren( - parentInstance._nativeTag, // containerID - [], // moveFromIndices - [], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [index], // removeAtIndices - ); -} - -export function removeChildFromContainer( - parentInstance: Container, - child: Instance | TextInstance, -): void { - recursivelyUncacheFiberNode(child); - UIManager.manageChildren( - parentInstance.containerTag, // containerID - [], // moveFromIndices - [], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [0], // removeAtIndices - ); -} - -export function resetTextContent(instance: Instance): void { - // Noop -} - -export function hideInstance(instance: Instance): void { - const viewConfig = instance.viewConfig; - const updatePayload = create( - {style: {display: 'none'}}, - viewConfig.validAttributes, - ); - UIManager.updateView( - instance._nativeTag, - viewConfig.uiViewClassName, - updatePayload, - ); -} - -export function hideTextInstance(textInstance: TextInstance): void { - throw new Error('Not yet implemented.'); -} - -export function unhideInstance(instance: Instance, props: Props): void { - const viewConfig = instance.viewConfig; - const updatePayload = diff( - {...props, style: [props.style, {display: 'none'}]}, - props, - viewConfig.validAttributes, - ); - UIManager.updateView( - instance._nativeTag, - viewConfig.uiViewClassName, - updatePayload, - ); -} - -export function applyViewTransitionName( - instance: Instance, - name: string, - className: ?string, -): void { - // Not yet implemented -} - -export function restoreViewTransitionName( - instance: Instance, - props: Props, -): void { - // Not yet implemented -} - -export function cancelViewTransitionName( - instance: Instance, - name: string, - props: Props, -): void { - // Not yet implemented -} - -export function cancelRootViewTransitionName(rootContainer: Container): void { - // Not yet implemented -} - -export function restoreRootViewTransitionName(rootContainer: Container): void { - // Not yet implemented -} - -export function cloneRootViewTransitionContainer( - rootContainer: Container, -): Instance { - throw new Error('Not implemented.'); -} - -export function removeRootViewTransitionClone( - rootContainer: Container, - clone: Instance, -): void { - throw new Error('Not implemented.'); -} - -export type InstanceMeasurement = null; - -export function measureInstance(instance: Instance): InstanceMeasurement { - // This heuristic is better implemented at the native layer. - return null; -} - -export function measureClonedInstance(instance: Instance): InstanceMeasurement { - return null; -} - -export function wasInstanceInViewport( - measurement: InstanceMeasurement, -): boolean { - return true; -} - -export function hasInstanceChanged( - oldMeasurement: InstanceMeasurement, - newMeasurement: InstanceMeasurement, -): boolean { - return false; -} - -export function hasInstanceAffectedParent( - oldMeasurement: InstanceMeasurement, - newMeasurement: InstanceMeasurement, -): boolean { - return false; -} - -export function startViewTransition( - suspendedState: null | SuspendedState, - rootContainer: Container, - transitionTypes: null | TransitionTypes, - mutationCallback: () => void, - layoutCallback: () => void, - afterMutationCallback: () => void, - spawnedWorkCallback: () => void, - passiveCallback: () => mixed, - errorCallback: mixed => void, - blockedCallback: string => void, // Profiling-only - finishedAnimation: () => void, // Profiling-only -): null | RunningViewTransition { - mutationCallback(); - layoutCallback(); - // Skip afterMutationCallback(). We don't need it since we're not animating. - spawnedWorkCallback(); - if (enableProfilerTimer) { - finishedAnimation(); - } - // Skip passiveCallback(). Spawned work will schedule a task. - return null; -} - -export type RunningViewTransition = null; - -export function startGestureTransition( - suspendedState: null | SuspendedState, - rootContainer: Container, - timeline: GestureTimeline, - rangeStart: number, - rangeEnd: number, - transitionTypes: null | TransitionTypes, - mutationCallback: () => void, - animateCallback: () => void, - errorCallback: mixed => void, - finishedAnimation: () => void, // Profiling-only -): null | RunningViewTransition { - mutationCallback(); - animateCallback(); - if (enableProfilerTimer) { - finishedAnimation(); - } - return null; -} - -export function stopViewTransition(transition: RunningViewTransition) {} - -export function addViewTransitionFinishedListener( - transition: RunningViewTransition, - callback: () => void, -) { - callback(); -} - -export type ViewTransitionInstance = null | {name: string, ...}; - -export function createViewTransitionInstance( - name: string, -): ViewTransitionInstance { - return null; -} - -export type GestureTimeline = null; - -export function getCurrentGestureOffset(provider: GestureTimeline): number { - throw new Error( - 'startGestureTransition is not yet supported in React Native.', - ); -} - -export function clearContainer(container: Container): void { - // TODO Implement this for React Native - // UIManager does not expose a "remove all" type method. -} - -export function unhideTextInstance( - textInstance: TextInstance, - text: string, -): void { - throw new Error('Not yet implemented.'); -} - -export {getClosestInstanceFromNode as getInstanceFromNode}; - -export function beforeActiveInstanceBlur(internalInstanceHandle: Object) { - // noop -} - -export function afterActiveInstanceBlur() { - // noop -} - -export function preparePortalMount(portalInstance: Instance): void { - // noop -} - -export function detachDeletedInstance(node: Instance): void { - // noop -} - -export function requestPostPaintCallback(callback: (time: number) => void) { - // noop -} - -export function maySuspendCommit(type: Type, props: Props): boolean { - return false; -} - -export function maySuspendCommitOnUpdate( - type: Type, - oldProps: Props, - newProps: Props, -): boolean { - return false; -} - -export function maySuspendCommitInSyncRender( - type: Type, - props: Props, -): boolean { - return false; -} - -export function preloadInstance( - instance: Instance, - type: Type, - props: Props, -): boolean { - // Return false to indicate it's already loaded - return true; -} - -export opaque type SuspendedState = null; - -export function startSuspendingCommit(): SuspendedState { - return null; -} - -export function suspendInstance( - state: SuspendedState, - instance: Instance, - type: Type, - props: Props, -): void {} - -export function suspendOnActiveViewTransition( - state: SuspendedState, - container: Container, -): void {} - -export function waitForCommitToBeReady( - state: SuspendedState, - timeoutOffset: number, -): null { - return null; -} - -export function getSuspendedCommitReason( - state: SuspendedState, - rootContainer: Container, -): null | string { - return null; -} - -export const NotPendingTransition: TransitionStatus = null; -export const HostTransitionContext: ReactContext = { - $$typeof: REACT_CONTEXT_TYPE, - Provider: (null: any), - Consumer: (null: any), - _currentValue: NotPendingTransition, - _currentValue2: NotPendingTransition, - _threadCount: 0, -}; - -export type FormInstance = Instance; -export function resetFormInstance(form: Instance): void {} diff --git a/packages/react-native-renderer/src/ReactNativeAttributePayload.js b/packages/react-native-renderer/src/ReactNativeAttributePayload.js deleted file mode 100644 index 68ba068308b9..000000000000 --- a/packages/react-native-renderer/src/ReactNativeAttributePayload.js +++ /dev/null @@ -1,492 +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 - */ - -// Modules provided by RN: -import { - deepDiffer, - flattenStyle, -} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; -import isArray from 'shared/isArray'; - -import type {AttributeConfiguration} from './ReactNativeTypes'; - -const emptyObject = {}; - -/** - * Create a payload that contains all the updates between two sets of props. - * - * These helpers are all encapsulated into a single module, because they use - * mutation as a performance optimization which leads to subtle shared - * dependencies between the code paths. To avoid this mutable state leaking - * across modules, I've kept them isolated to this module. - */ - -type NestedNode = Array | Object; - -// Tracks removed keys -let removedKeys: {[string]: boolean} | null = null; -let removedKeyCount = 0; - -const deepDifferOptions = { - unsafelyIgnoreFunctions: true, -}; - -function defaultDiffer(prevProp: mixed, nextProp: mixed): boolean { - if (typeof nextProp !== 'object' || nextProp === null) { - // Scalars have already been checked for equality - return true; - } else { - // For objects and arrays, the default diffing algorithm is a deep compare - return deepDiffer(prevProp, nextProp, deepDifferOptions); - } -} - -function restoreDeletedValuesInNestedArray( - updatePayload: Object, - node: NestedNode, - validAttributes: AttributeConfiguration, -) { - if (isArray(node)) { - let i = node.length; - while (i-- && removedKeyCount > 0) { - restoreDeletedValuesInNestedArray( - updatePayload, - node[i], - validAttributes, - ); - } - } else if (node && removedKeyCount > 0) { - const obj = node; - for (const propKey in removedKeys) { - // $FlowFixMe[incompatible-use] found when upgrading Flow - if (!removedKeys[propKey]) { - continue; - } - let nextProp = obj[propKey]; - if (nextProp === undefined) { - continue; - } - - const attributeConfig = validAttributes[propKey]; - if (!attributeConfig) { - continue; // not a valid native prop - } - - if (typeof nextProp === 'function') { - // $FlowFixMe[incompatible-type] found when upgrading Flow - nextProp = true; - } - if (typeof nextProp === 'undefined') { - // $FlowFixMe[incompatible-type] found when upgrading Flow - nextProp = null; - } - - if (typeof attributeConfig !== 'object') { - // case: !Object is the default case - updatePayload[propKey] = nextProp; - } else if ( - typeof attributeConfig.diff === 'function' || - typeof attributeConfig.process === 'function' - ) { - // case: CustomAttributeConfiguration - const nextValue = - typeof attributeConfig.process === 'function' - ? attributeConfig.process(nextProp) - : nextProp; - updatePayload[propKey] = nextValue; - } - // $FlowFixMe[incompatible-use] found when upgrading Flow - removedKeys[propKey] = false; - removedKeyCount--; - } - } -} - -function diffNestedArrayProperty( - updatePayload: null | Object, - prevArray: Array, - nextArray: Array, - validAttributes: AttributeConfiguration, -): null | Object { - const minLength = - prevArray.length < nextArray.length ? prevArray.length : nextArray.length; - let i; - for (i = 0; i < minLength; i++) { - // Diff any items in the array in the forward direction. Repeated keys - // will be overwritten by later values. - updatePayload = diffNestedProperty( - updatePayload, - prevArray[i], - nextArray[i], - validAttributes, - ); - } - for (; i < prevArray.length; i++) { - // Clear out all remaining properties. - updatePayload = clearNestedProperty( - updatePayload, - prevArray[i], - validAttributes, - ); - } - for (; i < nextArray.length; i++) { - // Add all remaining properties. - updatePayload = addNestedProperty( - updatePayload, - nextArray[i], - validAttributes, - ); - } - return updatePayload; -} - -function diffNestedProperty( - updatePayload: null | Object, - prevProp: NestedNode, - nextProp: NestedNode, - validAttributes: AttributeConfiguration, -): null | Object { - if (!updatePayload && prevProp === nextProp) { - // If no properties have been added, then we can bail out quickly on object - // equality. - return updatePayload; - } - - if (!prevProp || !nextProp) { - if (nextProp) { - return addNestedProperty(updatePayload, nextProp, validAttributes); - } - if (prevProp) { - return clearNestedProperty(updatePayload, prevProp, validAttributes); - } - return updatePayload; - } - - if (!isArray(prevProp) && !isArray(nextProp)) { - // Both are leaves, we can diff the leaves. - return diffProperties(updatePayload, prevProp, nextProp, validAttributes); - } - - if (isArray(prevProp) && isArray(nextProp)) { - // Both are arrays, we can diff the arrays. - return diffNestedArrayProperty( - updatePayload, - prevProp, - nextProp, - validAttributes, - ); - } - - if (isArray(prevProp)) { - return diffProperties( - updatePayload, - flattenStyle(prevProp), - nextProp, - validAttributes, - ); - } - - return diffProperties( - updatePayload, - prevProp, - flattenStyle(nextProp), - validAttributes, - ); -} - -/** - * addNestedProperty takes a single set of props and valid attribute - * attribute configurations. It processes each prop and adds it to the - * updatePayload. - */ -function addNestedProperty( - updatePayload: null | Object, - nextProp: NestedNode, - validAttributes: AttributeConfiguration, -): $FlowFixMe { - if (!nextProp) { - return updatePayload; - } - - if (!isArray(nextProp)) { - // Add each property of the leaf. - return addProperties(updatePayload, nextProp, validAttributes); - } - - for (let i = 0; i < nextProp.length; i++) { - // Add all the properties of the array. - updatePayload = addNestedProperty( - updatePayload, - nextProp[i], - validAttributes, - ); - } - - return updatePayload; -} - -/** - * clearNestedProperty takes a single set of props and valid attributes. It - * adds a null sentinel to the updatePayload, for each prop key. - */ -function clearNestedProperty( - updatePayload: null | Object, - prevProp: NestedNode, - validAttributes: AttributeConfiguration, -): null | Object { - if (!prevProp) { - return updatePayload; - } - - if (!isArray(prevProp)) { - // Add each property of the leaf. - return clearProperties(updatePayload, prevProp, validAttributes); - } - - for (let i = 0; i < prevProp.length; i++) { - // Add all the properties of the array. - updatePayload = clearNestedProperty( - updatePayload, - prevProp[i], - validAttributes, - ); - } - return updatePayload; -} - -/** - * diffProperties takes two sets of props and a set of valid attributes - * and write to updatePayload the values that changed or were deleted. - * If no updatePayload is provided, a new one is created and returned if - * anything changed. - */ -function diffProperties( - updatePayload: null | Object, - prevProps: Object, - nextProps: Object, - validAttributes: AttributeConfiguration, -): null | Object { - let attributeConfig; - let nextProp; - let prevProp; - - for (const propKey in nextProps) { - attributeConfig = validAttributes[propKey]; - if (!attributeConfig) { - continue; // not a valid native prop - } - - prevProp = prevProps[propKey]; - nextProp = nextProps[propKey]; - - // functions are converted to booleans as markers that the associated - // events should be sent from native. - if (typeof nextProp === 'function') { - nextProp = (true: any); - // If nextProp is not a function, then don't bother changing prevProp - // since nextProp will win and go into the updatePayload regardless. - if (typeof prevProp === 'function') { - prevProp = (true: any); - } - } - - // An explicit value of undefined is treated as a null because it overrides - // any other preceding value. - if (typeof nextProp === 'undefined') { - nextProp = (null: any); - if (typeof prevProp === 'undefined') { - prevProp = (null: any); - } - } - - if (removedKeys) { - removedKeys[propKey] = false; - } - - if (updatePayload && updatePayload[propKey] !== undefined) { - // Something else already triggered an update to this key because another - // value diffed. Since we're now later in the nested arrays our value is - // more important so we need to calculate it and override the existing - // value. It doesn't matter if nothing changed, we'll set it anyway. - - // Pattern match on: attributeConfig - if (typeof attributeConfig !== 'object') { - // case: !Object is the default case - updatePayload[propKey] = nextProp; - } else if ( - typeof attributeConfig.diff === 'function' || - typeof attributeConfig.process === 'function' - ) { - // case: CustomAttributeConfiguration - const nextValue = - typeof attributeConfig.process === 'function' - ? attributeConfig.process(nextProp) - : nextProp; - updatePayload[propKey] = nextValue; - } - continue; - } - - if (prevProp === nextProp) { - continue; // nothing changed - } - - // Pattern match on: attributeConfig - if (typeof attributeConfig !== 'object') { - // case: !Object is the default case - if (defaultDiffer(prevProp, nextProp)) { - // a normal leaf has changed - (updatePayload || (updatePayload = ({}: {[string]: $FlowFixMe})))[ - propKey - ] = nextProp; - } - } else if ( - typeof attributeConfig.diff === 'function' || - typeof attributeConfig.process === 'function' - ) { - // case: CustomAttributeConfiguration - const shouldUpdate = - prevProp === undefined || - (typeof attributeConfig.diff === 'function' - ? attributeConfig.diff(prevProp, nextProp) - : defaultDiffer(prevProp, nextProp)); - if (shouldUpdate) { - const nextValue = - typeof attributeConfig.process === 'function' - ? // $FlowFixMe[incompatible-use] found when upgrading Flow - attributeConfig.process(nextProp) - : nextProp; - (updatePayload || (updatePayload = ({}: {[string]: $FlowFixMe})))[ - propKey - ] = nextValue; - } - } else { - // default: fallthrough case when nested properties are defined - removedKeys = null; - removedKeyCount = 0; - // We think that attributeConfig is not CustomAttributeConfiguration at - // this point so we assume it must be AttributeConfiguration. - updatePayload = diffNestedProperty( - updatePayload, - prevProp, - nextProp, - ((attributeConfig: any): AttributeConfiguration), - ); - if (removedKeyCount > 0 && updatePayload) { - restoreDeletedValuesInNestedArray( - updatePayload, - nextProp, - ((attributeConfig: any): AttributeConfiguration), - ); - removedKeys = null; - } - } - } - - // Also iterate through all the previous props to catch any that have been - // removed and make sure native gets the signal so it can reset them to the - // default. - for (const propKey in prevProps) { - if (nextProps[propKey] !== undefined) { - continue; // we've already covered this key in the previous pass - } - attributeConfig = validAttributes[propKey]; - if (!attributeConfig) { - continue; // not a valid native prop - } - - if (updatePayload && updatePayload[propKey] !== undefined) { - // This was already updated to a diff result earlier. - continue; - } - - prevProp = prevProps[propKey]; - if (prevProp === undefined) { - continue; // was already empty anyway - } - // Pattern match on: attributeConfig - if ( - typeof attributeConfig !== 'object' || - typeof attributeConfig.diff === 'function' || - typeof attributeConfig.process === 'function' - ) { - // case: CustomAttributeConfiguration | !Object - // Flag the leaf property for removal by sending a sentinel. - (updatePayload || (updatePayload = ({}: {[string]: $FlowFixMe})))[ - propKey - ] = null; - if (!removedKeys) { - removedKeys = ({}: {[string]: boolean}); - } - if (!removedKeys[propKey]) { - removedKeys[propKey] = true; - removedKeyCount++; - } - } else { - // default: - // This is a nested attribute configuration where all the properties - // were removed so we need to go through and clear out all of them. - updatePayload = clearNestedProperty( - updatePayload, - prevProp, - ((attributeConfig: any): AttributeConfiguration), - ); - } - } - return updatePayload; -} - -/** - * addProperties adds all the valid props to the payload after being processed. - */ -function addProperties( - updatePayload: null | Object, - props: Object, - validAttributes: AttributeConfiguration, -): null | Object { - // TODO: Fast path - return diffProperties(updatePayload, emptyObject, props, validAttributes); -} - -/** - * clearProperties clears all the previous props by adding a null sentinel - * to the payload for each valid key. - */ -function clearProperties( - updatePayload: null | Object, - prevProps: Object, - validAttributes: AttributeConfiguration, -): null | Object { - // TODO: Fast path - return diffProperties(updatePayload, prevProps, emptyObject, validAttributes); -} - -export function create( - props: Object, - validAttributes: AttributeConfiguration, -): null | Object { - return addProperties( - null, // updatePayload - props, - validAttributes, - ); -} - -export function diff( - prevProps: Object, - nextProps: Object, - validAttributes: AttributeConfiguration, -): null | Object { - return diffProperties( - null, // updatePayload - prevProps, - nextProps, - validAttributes, - ); -} diff --git a/packages/react-native-renderer/src/ReactNativeComponentTree.js b/packages/react-native-renderer/src/ReactNativeComponentTree.js deleted file mode 100644 index bd3f1f44146a..000000000000 --- a/packages/react-native-renderer/src/ReactNativeComponentTree.js +++ /dev/null @@ -1,52 +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. - */ - -const instanceCache = new Map(); -const instanceProps = new Map(); - -export function precacheFiberNode(hostInst, tag) { - instanceCache.set(tag, hostInst); -} - -export function uncacheFiberNode(tag) { - instanceCache.delete(tag); - instanceProps.delete(tag); -} - -function getInstanceFromTag(tag) { - return instanceCache.get(tag) || null; -} - -function getTagFromInstance(inst) { - let nativeInstance = inst.stateNode; - let tag = nativeInstance._nativeTag; - if (tag === undefined && nativeInstance.canonical != null) { - // For compatibility with Fabric - tag = nativeInstance.canonical.nativeTag; - nativeInstance = nativeInstance.canonical.publicInstance; - } - - if (!tag) { - throw new Error('All native instances should have a tag.'); - } - - return nativeInstance; -} - -export { - getInstanceFromTag as getClosestInstanceFromNode, - getInstanceFromTag as getInstanceFromNode, - getTagFromInstance as getNodeFromInstance, -}; - -export function getFiberCurrentPropsFromNode(stateNode) { - return instanceProps.get(stateNode._nativeTag) || null; -} - -export function updateFiberProps(tag, props) { - instanceProps.set(tag, props); -} diff --git a/packages/react-native-renderer/src/ReactNativeEventEmitter.js b/packages/react-native-renderer/src/ReactNativeEventEmitter.js deleted file mode 100644 index ebac054de938..000000000000 --- a/packages/react-native-renderer/src/ReactNativeEventEmitter.js +++ /dev/null @@ -1,243 +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 - */ - -import type { - AnyNativeEvent, - LegacyPluginModule, -} from './legacy-events/PluginModuleType'; -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; -import type {ReactSyntheticEvent} from './legacy-events/ReactSyntheticEventType'; -import type {TopLevelType} from './legacy-events/TopLevelEventTypes'; - -import { - registrationNameModules, - plugins, -} from './legacy-events/EventPluginRegistry'; -import {batchedUpdates} from './legacy-events/ReactGenericBatching'; -import {runEventsInBatch} from './legacy-events/EventBatching'; -import getListener from './ReactNativeGetListener'; -import accumulateInto from './legacy-events/accumulateInto'; - -import {getInstanceFromNode} from './ReactNativeComponentTree'; - -export {getListener, registrationNameModules as registrationNames}; - -/** - * Version of `ReactBrowserEventEmitter` that works on the receiving side of a - * serialized worker boundary. - */ - -// Shared default empty native event - conserve memory. -const EMPTY_NATIVE_EVENT = (({}: any): AnyNativeEvent); - -/** - * Selects a subsequence of `Touch`es, without destroying `touches`. - * - * @param {Array} touches Deserialized touch objects. - * @param {Array} indices Indices by which to pull subsequence. - * @return {Array} Subsequence of touch objects. - */ -// $FlowFixMe[missing-local-annot] -function touchSubsequence(touches, indices) { - const ret = []; - for (let i = 0; i < indices.length; i++) { - ret.push(touches[indices[i]]); - } - return ret; -} - -/** - * TODO: Pool all of this. - * - * Destroys `touches` by removing touch objects at indices `indices`. This is - * to maintain compatibility with W3C touch "end" events, where the active - * touches don't include the set that has just been "ended". - * - * @param {Array} touches Deserialized touch objects. - * @param {Array} indices Indices to remove from `touches`. - * @return {Array} Subsequence of removed touch objects. - */ -function removeTouchesAtIndices( - touches: Array, - indices: Array, -): Array { - const rippedOut = []; - // use an unsafe downcast to alias to nullable elements, - // so we can delete and then compact. - const temp: Array = (touches: Array); - for (let i = 0; i < indices.length; i++) { - const index = indices[i]; - rippedOut.push(touches[index]); - temp[index] = null; - } - let fillAt = 0; - for (let j = 0; j < temp.length; j++) { - const cur = temp[j]; - if (cur !== null) { - temp[fillAt++] = cur; - } - } - temp.length = fillAt; - return rippedOut; -} - -/** - * Internal version of `receiveEvent` in terms of normalized (non-tag) - * `rootNodeID`. - * - * @see receiveEvent. - * - * @param {rootNodeID} rootNodeID React root node ID that event occurred on. - * @param {TopLevelType} topLevelType Top level type of event. - * @param {?object} nativeEventParam Object passed from native. - */ -function _receiveRootNodeIDEvent( - rootNodeID: number, - topLevelType: TopLevelType, - nativeEventParam: ?AnyNativeEvent, -) { - const nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT; - const inst = getInstanceFromNode(rootNodeID); - - let target = null; - if (inst != null) { - target = inst.stateNode; - } - - batchedUpdates(function () { - runExtractedPluginEventsInBatch(topLevelType, inst, nativeEvent, target); - }); - // React Native doesn't use ReactControlledComponent but if it did, here's - // where it would do it. -} - -/** - * Allows registered plugins an opportunity to extract events from top-level - * native browser events. - * - * @return {*} An accumulation of synthetic events. - * @internal - */ -function extractPluginEvents( - topLevelType: TopLevelType, - targetInst: null | Fiber, - nativeEvent: AnyNativeEvent, - nativeEventTarget: null | EventTarget, -): Array | ReactSyntheticEvent | null { - let events: Array | ReactSyntheticEvent | null = null; - const legacyPlugins = ((plugins: any): Array< - LegacyPluginModule, - >); - for (let i = 0; i < legacyPlugins.length; i++) { - // Not every plugin in the ordering may be loaded at runtime. - const possiblePlugin = legacyPlugins[i]; - if (possiblePlugin) { - const extractedEvents = possiblePlugin.extractEvents( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget, - ); - if (extractedEvents) { - events = accumulateInto(events, extractedEvents); - } - } - } - return events; -} - -function runExtractedPluginEventsInBatch( - topLevelType: TopLevelType, - targetInst: null | Fiber, - nativeEvent: AnyNativeEvent, - nativeEventTarget: null | EventTarget, -) { - const events = extractPluginEvents( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget, - ); - runEventsInBatch(events); -} - -/** - * Publicly exposed method on module for native objc to invoke when a top - * level event is extracted. - * @param {rootNodeID} rootNodeID React root node ID that event occurred on. - * @param {TopLevelType} topLevelType Top level type of event. - * @param {object} nativeEventParam Object passed from native. - */ -export function receiveEvent( - rootNodeID: number, - topLevelType: TopLevelType, - nativeEventParam: AnyNativeEvent, -) { - _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam); -} - -/** - * Simple multi-wrapper around `receiveEvent` that is intended to receive an - * efficient representation of `Touch` objects, and other information that - * can be used to construct W3C compliant `Event` and `Touch` lists. - * - * This may create dispatch behavior that differs than web touch handling. We - * loop through each of the changed touches and receive it as a single event. - * So two `touchStart`/`touchMove`s that occur simultaneously are received as - * two separate touch event dispatches - when they arguably should be one. - * - * This implementation reuses the `Touch` objects themselves as the `Event`s - * since we dispatch an event for each touch (though that might not be spec - * compliant). The main purpose of reusing them is to save allocations. - * - * TODO: Dispatch multiple changed touches in one event. The bubble path - * could be the first common ancestor of all the `changedTouches`. - * - * One difference between this behavior and W3C spec: cancelled touches will - * not appear in `.touches`, or in any future `.touches`, though they may - * still be "actively touching the surface". - * - * Web desktop polyfills only need to construct a fake touch event with - * identifier 0, also abandoning traditional click handlers. - */ -export function receiveTouches( - eventTopLevelType: TopLevelType, - touches: Array, - changedIndices: Array, -) { - const changedTouches = - eventTopLevelType === 'topTouchEnd' || - eventTopLevelType === 'topTouchCancel' - ? removeTouchesAtIndices(touches, changedIndices) - : touchSubsequence(touches, changedIndices); - - for (let jj = 0; jj < changedTouches.length; jj++) { - const touch = changedTouches[jj]; - // Touch objects can fulfill the role of `DOM` `Event` objects if we set - // the `changedTouches`/`touches`. This saves allocations. - touch.changedTouches = changedTouches; - touch.touches = touches; - const nativeEvent = touch; - let rootNodeID = null; - const target = nativeEvent.target; - if (target !== null && target !== undefined) { - if (target < 1) { - if (__DEV__) { - console.error( - 'A view is reporting that a touch occurred on tag zero.', - ); - } - } else { - rootNodeID = target; - } - } - // $FlowFixMe[incompatible-call] Shouldn't we *not* call it if rootNodeID is null? - _receiveRootNodeIDEvent(rootNodeID, eventTopLevelType, nativeEvent); - } -} diff --git a/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js b/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js deleted file mode 100644 index 7a253304fff0..000000000000 --- a/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js +++ /dev/null @@ -1,128 +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 - */ - -import type {ViewConfig} from './ReactNativeTypes'; -import type { - LegacyPublicInstance, - MeasureOnSuccessCallback, - MeasureInWindowOnSuccessCallback, - MeasureLayoutOnSuccessCallback, -} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; -import type {Instance} from './ReactFiberConfigNative'; - -// Modules provided by RN: -import { - TextInputState, - UIManager, -} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - -import {create} from './ReactNativeAttributePayload'; -import { - mountSafeCallback_NOT_REALLY_SAFE, - warnForStyleProps, -} from './NativeMethodsMixinUtils'; - -class ReactNativeFiberHostComponent implements LegacyPublicInstance { - _children: Array; - _nativeTag: number; - _internalFiberInstanceHandleDEV: Object; - viewConfig: ViewConfig; - - constructor( - tag: number, - viewConfig: ViewConfig, - internalInstanceHandleDEV: Object, - ) { - this._nativeTag = tag; - this._children = []; - this.viewConfig = viewConfig; - if (__DEV__) { - this._internalFiberInstanceHandleDEV = internalInstanceHandleDEV; - } - } - - blur() { - TextInputState.blurTextInput(this); - } - - focus() { - TextInputState.focusTextInput(this); - } - - measure(callback: MeasureOnSuccessCallback) { - UIManager.measure( - this._nativeTag, - mountSafeCallback_NOT_REALLY_SAFE(this, callback), - ); - } - - measureInWindow(callback: MeasureInWindowOnSuccessCallback) { - UIManager.measureInWindow( - this._nativeTag, - mountSafeCallback_NOT_REALLY_SAFE(this, callback), - ); - } - - measureLayout( - relativeToNativeNode: number | LegacyPublicInstance, - onSuccess: MeasureLayoutOnSuccessCallback, - onFail?: () => void /* currently unused */, - ) { - let relativeNode: ?number; - - if (typeof relativeToNativeNode === 'number') { - // Already a node handle - relativeNode = relativeToNativeNode; - } else { - const nativeNode: ReactNativeFiberHostComponent = - (relativeToNativeNode: any); - if (nativeNode._nativeTag) { - relativeNode = nativeNode._nativeTag; - } - } - - if (relativeNode == null) { - if (__DEV__) { - console.error( - 'ref.measureLayout must be called with a node handle or a ref to a native component.', - ); - } - - return; - } - - UIManager.measureLayout( - this._nativeTag, - relativeNode, - mountSafeCallback_NOT_REALLY_SAFE(this, onFail), - mountSafeCallback_NOT_REALLY_SAFE(this, onSuccess), - ); - } - - setNativeProps(nativeProps: Object) { - if (__DEV__) { - warnForStyleProps(nativeProps, this.viewConfig.validAttributes); - } - - const updatePayload = create(nativeProps, this.viewConfig.validAttributes); - - // Avoid the overhead of bridge calls if there's no update. - // This is an expensive no-op for Android, and causes an unnecessary - // view invalidation for certain components (eg RCTTextInput) on iOS. - if (updatePayload != null) { - UIManager.updateView( - this._nativeTag, - this.viewConfig.uiViewClassName, - updatePayload, - ); - } - } -} - -export default ReactNativeFiberHostComponent; diff --git a/packages/react-native-renderer/src/ReactNativeFiberInspector.js b/packages/react-native-renderer/src/ReactNativeFiberInspector.js index e8be83f9a9f4..5474c5d55052 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberInspector.js +++ b/packages/react-native-renderer/src/ReactNativeFiberInspector.js @@ -17,15 +17,8 @@ import { import getComponentNameFromType from 'shared/getComponentNameFromType'; import {HostComponent} from 'react-reconciler/src/ReactWorkTags'; // Module provided by RN: -import { - UIManager, - getNodeFromPublicInstance, -} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; -import {getClosestInstanceFromNode} from './ReactNativeComponentTree'; -import { - getNodeFromInternalInstanceHandle, - findNodeHandle, -} from './ReactNativePublicCompat'; +import {getNodeFromPublicInstance} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; +import {getNodeFromInternalInstanceHandle} from './ReactNativePublicCompat'; import {getStackByFiberInDevAndProd} from 'react-reconciler/src/ReactFiberComponentStack'; let getInspectorDataForInstance: ( @@ -43,7 +36,6 @@ if (__DEV__) { return { props: getHostProps(fiber), measure: callback => { - // If this is Fabric, we'll find a shadow node and use that to measure. const hostFiber = findCurrentHostFiber(fiber); const node = hostFiber != null && @@ -52,8 +44,6 @@ if (__DEV__) { if (node) { nativeFabricUIManager.measure(node, callback); - } else { - return UIManager.measure(getHostNode(fiber), callback); } }, }; @@ -61,22 +51,6 @@ if (__DEV__) { })); }; - const getHostNode = function (fiber: Fiber | null) { - let hostNode; - // look for children first for the hostNode - // as composite fibers do not have a hostNode - while (fiber) { - if (fiber.stateNode !== null && fiber.tag === HostComponent) { - hostNode = findNodeHandle(fiber.stateNode); - } - if (hostNode) { - return hostNode; - } - fiber = fiber.child; - } - return null; - }; - const getHostProps = function (fiber: Fiber) { const host = findCurrentHostFiber(fiber); if (host) { @@ -156,17 +130,6 @@ if (__DEV__) { }; } -function getInspectorDataForViewTag(viewTag: number): InspectorData { - if (__DEV__) { - const closestInstance = getClosestInstanceFromNode(viewTag); - return getInspectorDataForInstance(closestInstance); - } else { - throw new Error( - 'getInspectorDataForViewTag() is not available in production', - ); - } -} - function getInspectorDataForViewAtPoint( inspectedView: Object, locationX: number, @@ -222,25 +185,6 @@ function getInspectorDataForViewAtPoint( ); }, ); - } else if (inspectedView._internalFiberInstanceHandleDEV != null) { - // For Paper we fall back to the old strategy using the React tag. - UIManager.findSubviewIn( - findNodeHandle(inspectedView), - [locationX, locationY], - (nativeViewTag, left, top, width, height) => { - const inspectorData = getInspectorDataForInstance( - getClosestInstanceFromNode(nativeViewTag), - ); - callback({ - ...inspectorData, - pointerY: locationY, - frame: {left, top, width, height}, - touchedViewTag: nativeViewTag, - // $FlowExpectedError[incompatible-call] - closestPublicInstance: nativeViewTag, - }); - }, - ); } else { console.error( 'getInspectorDataForViewAtPoint expects to receive a host component', @@ -255,8 +199,4 @@ function getInspectorDataForViewAtPoint( } } -export { - getInspectorDataForInstance, - getInspectorDataForViewAtPoint, - getInspectorDataForViewTag, -}; +export {getInspectorDataForInstance, getInspectorDataForViewAtPoint}; diff --git a/packages/react-native-renderer/src/ReactNativeGlobalResponderHandler.js b/packages/react-native-renderer/src/ReactNativeGlobalResponderHandler.js deleted file mode 100644 index 5661daf9061b..000000000000 --- a/packages/react-native-renderer/src/ReactNativeGlobalResponderHandler.js +++ /dev/null @@ -1,24 +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 - */ - -// Module provided by RN: -import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - -const ReactNativeGlobalResponderHandler = { - onChange: function (from: any, to: any, blockNativeResponder: boolean) { - if (to !== null) { - const tag = to.stateNode._nativeTag; - UIManager.setJSResponder(tag, blockNativeResponder); - } else { - UIManager.clearJSResponder(); - } - }, -}; - -export default ReactNativeGlobalResponderHandler; diff --git a/packages/react-native-renderer/src/ReactNativeInjection.js b/packages/react-native-renderer/src/ReactNativeInjection.js deleted file mode 100644 index 6c598c35d76a..000000000000 --- a/packages/react-native-renderer/src/ReactNativeInjection.js +++ /dev/null @@ -1,41 +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 - */ - -import './ReactNativeInjectionShared'; - -import { - getFiberCurrentPropsFromNode, - getInstanceFromNode, - getNodeFromInstance, -} from './ReactNativeComponentTree'; -import {setComponentTree} from './legacy-events/EventPluginUtils'; -import {receiveEvent, receiveTouches} from './ReactNativeEventEmitter'; -import ReactNativeGlobalResponderHandler from './ReactNativeGlobalResponderHandler'; -import ResponderEventPlugin from './legacy-events/ResponderEventPlugin'; - -// Module provided by RN: -import {RCTEventEmitter} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - -/** - * Register the event emitter with the native bridge - */ -RCTEventEmitter.register({ - receiveEvent, - receiveTouches, -}); - -setComponentTree( - getFiberCurrentPropsFromNode, - getInstanceFromNode, - getNodeFromInstance, -); - -ResponderEventPlugin.injection.injectGlobalResponderHandler( - ReactNativeGlobalResponderHandler, -); diff --git a/packages/react-native-renderer/src/ReactNativePublicCompat.js b/packages/react-native-renderer/src/ReactNativePublicCompat.js index e0193ddc37e0..120ddd9e11ca 100644 --- a/packages/react-native-renderer/src/ReactNativePublicCompat.js +++ b/packages/react-native-renderer/src/ReactNativePublicCompat.js @@ -13,8 +13,6 @@ import type {PublicInstance} from 'react-native/Libraries/ReactPrivate/ReactNati // Modules provided by RN: import { - UIManager, - legacySendAccessibilityEvent, getNodeFromPublicInstance, getNativeTagFromPublicInstance, getInternalInstanceHandleFromPublicInstance, @@ -65,13 +63,6 @@ export function findHostInstance_DEPRECATED( return componentOrHandle.canonical.publicInstance; } - // For compatibility with legacy renderer instances - if (componentOrHandle._nativeTag) { - // $FlowFixMe[incompatible-exact] Necessary when running Flow on Fabric - // $FlowFixMe[incompatible-return] - return componentOrHandle; - } - let hostInstance; if (__DEV__) { hostInstance = findHostInstanceWithWarning( @@ -116,11 +107,6 @@ export function findNodeHandle(componentOrHandle: any): ?number { return componentOrHandle; } - // For compatibility with legacy renderer instances - if (componentOrHandle._nativeTag) { - return componentOrHandle._nativeTag; - } - // For compatibility with Fabric instances if ( componentOrHandle.canonical != null && @@ -150,11 +136,6 @@ export function findNodeHandle(componentOrHandle: any): ?number { return hostInstance; } - if (hostInstance._nativeTag != null) { - // $FlowFixMe[incompatible-return] For compatibility with legacy renderer instances - return hostInstance._nativeTag; - } - // $FlowFixMe[incompatible-call] Necessary when running Flow on the legacy renderer return getNativeTagFromPublicInstance(hostInstance); } @@ -164,49 +145,32 @@ export function dispatchCommand( command: string, args: Array, ) { - const nativeTag = - handle._nativeTag != null - ? handle._nativeTag - : getNativeTagFromPublicInstance(handle); - if (nativeTag == null) { + const node = getNodeFromPublicInstance(handle); + + if (node != null) { + nativeFabricUIManager.dispatchCommand(node, command, args); + } else { if (__DEV__) { console.error( "dispatchCommand was called with a ref that isn't a " + 'native component. Use React.forwardRef to get access to the underlying native component', ); } - return; } +} +export function sendAccessibilityEvent(handle: any, eventType: string) { const node = getNodeFromPublicInstance(handle); if (node != null) { - nativeFabricUIManager.dispatchCommand(node, command, args); + nativeFabricUIManager.sendAccessibilityEvent(node, eventType); } else { - UIManager.dispatchViewManagerCommand(nativeTag, command, args); - } -} - -export function sendAccessibilityEvent(handle: any, eventType: string) { - const nativeTag = - handle._nativeTag != null - ? handle._nativeTag - : getNativeTagFromPublicInstance(handle); - if (nativeTag == null) { if (__DEV__) { console.error( "sendAccessibilityEvent was called with a ref that isn't a " + 'native component. Use React.forwardRef to get access to the underlying native component', ); } - return; - } - - const node = getNodeFromPublicInstance(handle); - if (node != null) { - nativeFabricUIManager.sendAccessibilityEvent(node, eventType); - } else { - legacySendAccessibilityEvent(nativeTag, eventType); } } @@ -223,29 +187,11 @@ export function getNodeFromInternalInstanceHandle( ); } -// Remove this once Paper is no longer supported and DOM Node API are enabled by default in RN. export function isChildPublicInstance( parentInstance: PublicInstance, childInstance: PublicInstance, ): boolean { if (__DEV__) { - // Paper - if ( - // $FlowExpectedError[incompatible-type] - // $FlowExpectedError[prop-missing] Don't check via `instanceof ReactNativeFiberHostComponent`, so it won't be leaked to Fabric. - parentInstance._internalFiberInstanceHandleDEV && - // $FlowExpectedError[incompatible-type] - // $FlowExpectedError[prop-missing] Don't check via `instanceof ReactNativeFiberHostComponent`, so it won't be leaked to Fabric. - childInstance._internalFiberInstanceHandleDEV - ) { - return doesFiberContain( - // $FlowExpectedError[incompatible-call] - parentInstance._internalFiberInstanceHandleDEV, - // $FlowExpectedError[incompatible-call] - childInstance._internalFiberInstanceHandleDEV, - ); - } - const parentInternalInstanceHandle = // $FlowExpectedError[incompatible-call] Type for parentInstance should have been PublicInstance from ReactFiberConfigFabric. getInternalInstanceHandleFromPublicInstance(parentInstance); @@ -253,7 +199,6 @@ export function isChildPublicInstance( // $FlowExpectedError[incompatible-call] Type for childInstance should have been PublicInstance from ReactFiberConfigFabric. getInternalInstanceHandleFromPublicInstance(childInstance); - // Fabric if ( parentInternalInstanceHandle != null && childInternalInstanceHandle != null @@ -264,7 +209,6 @@ export function isChildPublicInstance( ); } - // Means that one instance is from Fabric and other is from Paper. return false; } else { throw new Error('isChildPublicInstance() is not available in production.'); diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js deleted file mode 100644 index c7f7a11dcc57..000000000000 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ /dev/null @@ -1,223 +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 - */ - -import type {ReactPortal, ReactNodeList} from 'shared/ReactTypes'; -import type {ElementRef, ElementType, MixedElement} from 'react'; -import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; -import type {RenderRootOptions} from './ReactNativeTypes'; -import type {Container} from 'react-reconciler/src/ReactFiberConfig'; - -import './ReactNativeInjection'; - -import { - batchedUpdates as batchedUpdatesImpl, - discreteUpdates, - createContainer, - updateContainer, - injectIntoDevTools, - getPublicRootInstance, - defaultOnUncaughtError, - defaultOnCaughtError, - defaultOnRecoverableError, -} from 'react-reconciler/src/ReactFiberReconciler'; -// TODO: direct imports like some-package/src/* are bad. Fix me. -import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal'; -import { - setBatchingImplementation, - batchedUpdates, -} from './legacy-events/ReactGenericBatching'; -// Modules provided by RN: -import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - -import {LegacyRoot} from 'react-reconciler/src/ReactRootTags'; -import { - findHostInstance_DEPRECATED, - findNodeHandle, - dispatchCommand, - sendAccessibilityEvent, - isChildPublicInstance, -} from './ReactNativePublicCompat'; - -import {disableLegacyMode} from 'shared/ReactFeatureFlags'; - -// Module provided by RN: -import {ReactFiberErrorDialog} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; - -import reactNativePackageVersion from 'shared/ReactVersion'; -import * as IsomorphicReactPackage from 'react'; - -const isomorphicReactPackageVersion = IsomorphicReactPackage.version; -if (isomorphicReactPackageVersion !== reactNativePackageVersion) { - throw new Error( - 'Incompatible React versions: The "react" and "react-native-renderer" packages must ' + - 'have the exact same version. Instead got:\n' + - ` - react: ${isomorphicReactPackageVersion}\n` + - ` - react-native-renderer: ${reactNativePackageVersion}\n` + - 'Learn more: https://react.dev/warnings/version-mismatch', - ); -} - -if (typeof ReactFiberErrorDialog.showErrorDialog !== 'function') { - throw new Error( - 'Expected ReactFiberErrorDialog.showErrorDialog to be a function.', - ); -} - -function nativeOnUncaughtError( - error: mixed, - errorInfo: {+componentStack?: ?string}, -): void { - const componentStack = - errorInfo.componentStack != null ? errorInfo.componentStack : ''; - const logError = ReactFiberErrorDialog.showErrorDialog({ - errorBoundary: null, - error, - componentStack, - }); - - // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. - if (logError === false) { - return; - } - - defaultOnUncaughtError(error, errorInfo); -} -function nativeOnCaughtError( - error: mixed, - errorInfo: { - +componentStack?: ?string, - +errorBoundary?: ?component(...props: any), - }, -): void { - const errorBoundary = errorInfo.errorBoundary; - const componentStack = - errorInfo.componentStack != null ? errorInfo.componentStack : ''; - const logError = ReactFiberErrorDialog.showErrorDialog({ - errorBoundary, - error, - componentStack, - }); - - // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. - if (logError === false) { - return; - } - - defaultOnCaughtError(error, errorInfo); -} -function nativeOnDefaultTransitionIndicator(): void | (() => void) { - // Native doesn't have a default indicator. -} - -function render( - element: MixedElement, - containerTag: number, - callback: ?() => void, - options: ?RenderRootOptions, -): ?ElementRef { - if (disableLegacyMode) { - throw new Error('render: Unsupported Legacy Mode API.'); - } - - let root = roots.get(containerTag); - - if (!root) { - // TODO: these defaults are for backwards compatibility. - // Once RN implements these options internally, - // we can remove the defaults and ReactFiberErrorDialog. - let onUncaughtError = nativeOnUncaughtError; - let onCaughtError = nativeOnCaughtError; - let onRecoverableError = defaultOnRecoverableError; - - if (options && options.onUncaughtError !== undefined) { - onUncaughtError = options.onUncaughtError; - } - if (options && options.onCaughtError !== undefined) { - onCaughtError = options.onCaughtError; - } - if (options && options.onRecoverableError !== undefined) { - onRecoverableError = options.onRecoverableError; - } - - const rootInstance: Container = { - containerTag, - // $FlowExpectedError[incompatible-type] the legacy renderer does not use public root instances - publicInstance: null, - }; - - // TODO (bvaughn): If we decide to keep the wrapper component, - // We could create a wrapper for containerTag as well to reduce special casing. - root = createContainer( - rootInstance, - LegacyRoot, - null, - false, - null, - '', - onUncaughtError, - onCaughtError, - onRecoverableError, - nativeOnDefaultTransitionIndicator, - null, - ); - roots.set(containerTag, root); - } - updateContainer(element, root, null, callback); - - return getPublicRootInstance(root); -} - -function unmountComponentAtNode(containerTag: number) { - const root = roots.get(containerTag); - if (root) { - // TODO: Is it safe to reset this now or should I wait since this unmount could be deferred? - updateContainer(null, root, null, () => { - roots.delete(containerTag); - }); - } -} - -function unmountComponentAtNodeAndRemoveContainer(containerTag: number) { - unmountComponentAtNode(containerTag); - - // Call back into native to remove all of the subviews from this container - UIManager.removeRootView(containerTag); -} - -function createPortal( - children: ReactNodeList, - containerTag: number, - key: ?string = null, -): ReactPortal { - return createPortalImpl(children, containerTag, null, key); -} - -setBatchingImplementation(batchedUpdatesImpl, discreteUpdates); - -const roots = new Map(); - -export { - // This is needed for implementation details of TouchableNativeFeedback - // Remove this once TouchableNativeFeedback doesn't use cloneElement - findHostInstance_DEPRECATED, - findNodeHandle, - dispatchCommand, - sendAccessibilityEvent, - render, - unmountComponentAtNode, - unmountComponentAtNodeAndRemoveContainer, - createPortal, - batchedUpdates as unstable_batchedUpdates, - // DEV-only: - isChildPublicInstance, -}; - -injectIntoDevTools(); diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index cfd026bc7d08..cc9a81795262 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -137,36 +137,6 @@ export type RenderRootOptions = { onDefaultTransitionIndicator?: () => void | (() => void), }; -/** - * Flat ReactNative renderer bundles are too big for Flow to parse efficiently. - * Provide minimal Flow typing for the high-level RN API and call it a day. - */ -export type ReactNativeType = { - findHostInstance_DEPRECATED( - componentOrHandle: ?(React.ElementRef | number), - ): ?PublicInstance, - findNodeHandle( - componentOrHandle: ?(React.ElementRef | number), - ): ?number, - isChildPublicInstance(parent: PublicInstance, child: PublicInstance): boolean, - dispatchCommand( - handle: PublicInstance, - command: string, - args: Array, - ): void, - sendAccessibilityEvent(handle: PublicInstance, eventType: string): void, - render( - element: React.MixedElement, - containerTag: number, - callback: ?() => void, - options: ?RenderRootOptions, - ): ?React.ElementRef, - unmountComponentAtNode(containerTag: number): void, - unmountComponentAtNodeAndRemoveContainer(containerTag: number): void, - +unstable_batchedUpdates: (fn: (T) => void, bookkeeping: T) => void, - ... -}; - export opaque type Node = mixed; export opaque type InternalInstanceHandle = mixed; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/BatchedBridge.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/BatchedBridge.js deleted file mode 100644 index 9083f2e6d188..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/BatchedBridge.js +++ /dev/null @@ -1,14 +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. - */ - -'use strict'; - -const BatchedBridge = { - registerCallableModule: jest.fn(), -}; - -module.exports = BatchedBridge; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ExceptionsManager.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ExceptionsManager.js deleted file mode 100644 index cd43a5b2de05..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ExceptionsManager.js +++ /dev/null @@ -1,12 +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. - */ - -'use strict'; - -module.exports = { - handleException: jest.fn(), -}; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/Platform.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/Platform.js deleted file mode 100644 index 5ea938a026b6..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/Platform.js +++ /dev/null @@ -1,11 +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. - */ -'use strict'; - -module.exports = { - OS: 'ios', -}; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RCTEventEmitter.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RCTEventEmitter.js deleted file mode 100644 index 8ddf594a4aa3..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/RCTEventEmitter.js +++ /dev/null @@ -1,14 +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. - */ - -'use strict'; - -const RCTEventEmitter = { - register: jest.fn(), -}; - -module.exports = RCTEventEmitter; 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 d8d1c240951e..28b48775b921 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 @@ -8,39 +8,15 @@ */ module.exports = { - get BatchedBridge() { - return require('./BatchedBridge.js'); - }, - get Platform() { - return require('./Platform'); - }, - get RCTEventEmitter() { - return require('./RCTEventEmitter'); - }, get ReactFiberErrorDialog() { return require('./ReactFiberErrorDialog'); }, get ReactNativeViewConfigRegistry() { return require('./ReactNativeViewConfigRegistry'); }, - get TextInputState() { - return require('./TextInputState'); - }, - get UIManager() { - return require('./UIManager'); - }, - get deepDiffer() { - return require('./deepDiffer'); - }, get deepFreezeAndThrowOnMutationInDev() { return require('./deepFreezeAndThrowOnMutationInDev'); }, - get flattenStyle() { - return require('./flattenStyle'); - }, - get legacySendAccessibilityEvent() { - return require('./legacySendAccessibilityEvent'); - }, get RawEventEmitter() { return require('./RawEventEmitter').default; }, diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/TextInputState.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/TextInputState.js deleted file mode 100644 index 43c18ed78a8a..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/TextInputState.js +++ /dev/null @@ -1,18 +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. - */ - -'use strict'; - -// Mock of the Native Hooks -// TODO: Should this move into the components themselves? E.g. focusable - -const TextInputState = { - blurTextInput: jest.fn(), - focusTextInput: jest.fn(), -}; - -module.exports = TextInputState; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/UIManager.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/UIManager.js deleted file mode 100644 index 2aba16330924..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/UIManager.js +++ /dev/null @@ -1,194 +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. - */ - -'use strict'; - -// Mock of the Native Hooks - -// Map of viewTag -> {children: [childTag], parent: ?parentTag} -const roots = []; -const views = new Map(); - -function autoCreateRoot(tag) { - // Seriously, this is how we distinguish roots in RN. - if (!views.has(tag) && tag % 10 === 1) { - roots.push(tag); - views.set(tag, { - children: [], - parent: null, - props: {}, - viewName: '', - }); - } -} - -function insertSubviewAtIndex(parent, child, index) { - const parentInfo = views.get(parent); - const childInfo = views.get(child); - - if (childInfo.parent !== null) { - throw new Error( - `Inserting view ${child} ${JSON.stringify( - childInfo.props, - )} which already has parent`, - ); - } - - if (0 > index || index > parentInfo.children.length) { - throw new Error( - `Invalid index ${index} for children ${parentInfo.children}`, - ); - } - - parentInfo.children.splice(index, 0, child); - childInfo.parent = parent; -} - -function removeChild(parent, child) { - const parentInfo = views.get(parent); - const childInfo = views.get(child); - const index = parentInfo.children.indexOf(child); - - if (index < 0) { - throw new Error(`Missing view ${child} during removal`); - } - - parentInfo.children.splice(index, 1); - childInfo.parent = null; -} - -const RCTUIManager = { - __dumpHierarchyForJestTestsOnly: function () { - function dumpSubtree(tag, indent) { - const info = views.get(tag); - let out = ''; - out += - ' '.repeat(indent) + info.viewName + ' ' + JSON.stringify(info.props); - // eslint-disable-next-line no-for-of-loops/no-for-of-loops - for (const child of info.children) { - out += '\n' + dumpSubtree(child, indent + 2); - } - return out; - } - return roots.map(tag => dumpSubtree(tag, 0)).join('\n'); - }, - clearJSResponder: jest.fn(), - createView: jest.fn(function createView(reactTag, viewName, rootTag, props) { - if (views.has(reactTag)) { - throw new Error(`Created two native views with tag ${reactTag}`); - } - - views.set(reactTag, { - children: [], - parent: null, - props: props, - viewName: viewName, - }); - }), - dispatchViewManagerCommand: jest.fn(), - sendAccessibilityEvent: jest.fn(), - setJSResponder: jest.fn(), - setChildren: jest.fn(function setChildren(parentTag, reactTags) { - autoCreateRoot(parentTag); - - // Native doesn't actually check this but it seems like a good idea - if (views.get(parentTag).children.length !== 0) { - throw new Error(`Calling .setChildren on nonempty view ${parentTag}`); - } - - // This logic ported from iOS (RCTUIManager.m) - reactTags.forEach((tag, i) => { - insertSubviewAtIndex(parentTag, tag, i); - }); - }), - manageChildren: jest.fn(function manageChildren( - parentTag, - moveFromIndices = [], - moveToIndices = [], - addChildReactTags = [], - addAtIndices = [], - removeAtIndices = [], - ) { - autoCreateRoot(parentTag); - - // This logic ported from iOS (RCTUIManager.m) - if (moveFromIndices.length !== moveToIndices.length) { - throw new Error( - `Mismatched move indices ${moveFromIndices} and ${moveToIndices}`, - ); - } - - if (addChildReactTags.length !== addAtIndices.length) { - throw new Error( - `Mismatched add indices ${addChildReactTags} and ${addAtIndices}`, - ); - } - - const parentInfo = views.get(parentTag); - const permanentlyRemovedChildren = removeAtIndices.map( - index => parentInfo.children[index], - ); - const temporarilyRemovedChildren = moveFromIndices.map( - index => parentInfo.children[index], - ); - permanentlyRemovedChildren.forEach(tag => removeChild(parentTag, tag)); - temporarilyRemovedChildren.forEach(tag => removeChild(parentTag, tag)); - permanentlyRemovedChildren.forEach(tag => { - views.delete(tag); - }); - // List of [index, tag] - const indicesToInsert = []; - temporarilyRemovedChildren.forEach((tag, i) => { - indicesToInsert.push([moveToIndices[i], temporarilyRemovedChildren[i]]); - }); - addChildReactTags.forEach((tag, i) => { - indicesToInsert.push([addAtIndices[i], addChildReactTags[i]]); - }); - indicesToInsert.sort((a, b) => a[0] - b[0]); - // eslint-disable-next-line no-for-of-loops/no-for-of-loops - for (const [i, tag] of indicesToInsert) { - insertSubviewAtIndex(parentTag, tag, i); - } - }), - updateView: jest.fn(), - removeSubviewsFromContainerWithID: jest.fn(function (parentTag) { - views.get(parentTag).children.forEach(tag => removeChild(parentTag, tag)); - }), - replaceExistingNonRootView: jest.fn(), - measure: jest.fn(function measure(tag, callback) { - if (typeof tag !== 'number') { - throw new Error(`Expected tag to be a number, was passed ${tag}`); - } - - callback(10, 10, 100, 100, 0, 0); - }), - measureInWindow: jest.fn(function measureInWindow(tag, callback) { - if (typeof tag !== 'number') { - throw new Error(`Expected tag to be a number, was passed ${tag}`); - } - - callback(10, 10, 100, 100); - }), - measureLayout: jest.fn( - function measureLayout(tag, relativeTag, fail, success) { - if (typeof tag !== 'number') { - throw new Error(`Expected tag to be a number, was passed ${tag}`); - } - - if (typeof relativeTag !== 'number') { - throw new Error( - `Expected relativeTag to be a number, was passed ${relativeTag}`, - ); - } - - success(1, 1, 100, 100); - }, - ), - __takeSnapshot: jest.fn(), -}; - -module.exports = RCTUIManager; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/flattenStyle.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/flattenStyle.js deleted file mode 100644 index 49bebf6f0759..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/flattenStyle.js +++ /dev/null @@ -1,14 +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. - */ - -'use strict'; - -// TODO: Move flattenStyle into react - -function flattenStyle() {} - -module.exports = flattenStyle; diff --git a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/legacySendAccessibilityEvent.js b/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/legacySendAccessibilityEvent.js deleted file mode 100644 index 8ff892ae3a5e..000000000000 --- a/packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/legacySendAccessibilityEvent.js +++ /dev/null @@ -1,10 +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. - */ - -'use strict'; - -module.exports = jest.fn(); 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 925103f2d75a..f0afbad5b4fc 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js @@ -1521,6 +1521,10 @@ describe('ReactFabric', () => { dangerouslyRetainedViewRef, 'eventTypeName', ); + assertConsoleErrorDev([ + "sendAccessibilityEvent was called with a ref that isn't a " + + 'native component. Use React.forwardRef to get access to the underlying native component', + ]); expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled(); }); diff --git a/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js deleted file mode 100644 index 4e0fcad9c80c..000000000000 --- a/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js +++ /dev/null @@ -1,231 +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. - * - * @emails react-core - * @jest-environment node - */ - -'use strict'; - -let React; -let ReactFabric; -let ReactNative; -let UIManager; -let createReactNativeComponentClass; -let ReactNativePrivateInterface; -let getNativeTagFromPublicInstance; - -describe('created with ReactFabric called with ReactNative', () => { - beforeEach(() => { - jest.resetModules(); - require('react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager'); - ReactNative = require('react-native-renderer'); - jest.resetModules(); - ReactNativePrivateInterface = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - UIManager = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').UIManager; - jest.mock('shared/ReactFeatureFlags', () => - require('shared/forks/ReactFeatureFlags.native-oss'), - ); - - React = require('react'); - ReactFabric = require('react-native-renderer/fabric'); - createReactNativeComponentClass = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') - .ReactNativeViewConfigRegistry.register; - getNativeTagFromPublicInstance = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').getNativeTagFromPublicInstance; - }); - - it('find Fabric instances with the RN renderer', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - class Component extends React.Component { - render() { - return ; - } - } - - ReactFabric.render(, 11); - - const instance = ReactNative.findHostInstance_DEPRECATED(ref.current); - expect(getNativeTagFromPublicInstance(instance)).toBe(2); - }); - - it('find Fabric nodes with the RN renderer', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - class Component extends React.Component { - render() { - return ; - } - } - - ReactFabric.render(, 11); - - const handle = ReactNative.findNodeHandle(ref.current); - expect(handle).toBe(2); - }); - - it('dispatches commands on Fabric nodes with the RN renderer', () => { - nativeFabricUIManager.dispatchCommand.mockClear(); - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - ReactFabric.render(, 11); - expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled(); - ReactNative.dispatchCommand(ref.current, 'myCommand', [10, 20]); - expect(nativeFabricUIManager.dispatchCommand).toHaveBeenCalledTimes(1); - expect(nativeFabricUIManager.dispatchCommand).toHaveBeenCalledWith( - expect.any(Object), - 'myCommand', - [10, 20], - ); - expect(UIManager.dispatchViewManagerCommand).not.toBeCalled(); - }); - - it('dispatches sendAccessibilityEvent on Fabric nodes with the RN renderer', () => { - nativeFabricUIManager.sendAccessibilityEvent.mockClear(); - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - ReactFabric.render(, 11); - expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled(); - ReactNative.sendAccessibilityEvent(ref.current, 'focus'); - expect(nativeFabricUIManager.sendAccessibilityEvent).toHaveBeenCalledTimes( - 1, - ); - expect(nativeFabricUIManager.sendAccessibilityEvent).toHaveBeenCalledWith( - expect.any(Object), - 'focus', - ); - expect(UIManager.sendAccessibilityEvent).not.toBeCalled(); - }); -}); - -describe('created with ReactNative called with ReactFabric', () => { - beforeEach(() => { - jest.resetModules(); - require('react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager'); - ReactFabric = require('react-native-renderer/fabric'); - jest.resetModules(); - UIManager = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').UIManager; - jest.mock('shared/ReactFeatureFlags', () => - require('shared/forks/ReactFeatureFlags.native-oss'), - ); - ReactNative = require('react-native-renderer'); - - React = require('react'); - createReactNativeComponentClass = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') - .ReactNativeViewConfigRegistry.register; - }); - - it('find Paper instances with the Fabric renderer', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - class Component extends React.Component { - render() { - return ; - } - } - - ReactNative.render(, 11); - - const instance = ReactFabric.findHostInstance_DEPRECATED(ref.current); - expect(instance._nativeTag).toBe(3); - }); - - it('find Paper nodes with the Fabric renderer', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - class Component extends React.Component { - render() { - return ; - } - } - - ReactNative.render(, 11); - - const handle = ReactFabric.findNodeHandle(ref.current); - expect(handle).toBe(3); - }); - - it('dispatches commands on Paper nodes with the Fabric renderer', () => { - UIManager.dispatchViewManagerCommand.mockReset(); - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - ReactNative.render(, 11); - expect(UIManager.dispatchViewManagerCommand).not.toBeCalled(); - ReactFabric.dispatchCommand(ref.current, 'myCommand', [10, 20]); - expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledTimes(1); - expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledWith( - expect.any(Number), - 'myCommand', - [10, 20], - ); - - expect(nativeFabricUIManager.dispatchCommand).not.toBeCalled(); - }); - - it('dispatches sendAccessibilityEvent on Paper nodes with the Fabric renderer', () => { - ReactNativePrivateInterface.legacySendAccessibilityEvent.mockReset(); - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - const ref = React.createRef(); - - ReactNative.render(, 11); - expect( - ReactNativePrivateInterface.legacySendAccessibilityEvent, - ).not.toBeCalled(); - ReactFabric.sendAccessibilityEvent(ref.current, 'focus'); - expect( - ReactNativePrivateInterface.legacySendAccessibilityEvent, - ).toHaveBeenCalledTimes(1); - expect( - ReactNativePrivateInterface.legacySendAccessibilityEvent, - ).toHaveBeenCalledWith(expect.any(Number), 'focus'); - - expect(nativeFabricUIManager.sendAccessibilityEvent).not.toBeCalled(); - }); -}); diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeAttributePayload-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeAttributePayload-test.internal.js deleted file mode 100644 index da43b939c6e1..000000000000 --- a/packages/react-native-renderer/src/__tests__/ReactNativeAttributePayload-test.internal.js +++ /dev/null @@ -1,287 +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. - * - * @jest-environment node - */ -'use strict'; - -const ReactNativeAttributePayload = require('../ReactNativeAttributePayload'); - -const diff = ReactNativeAttributePayload.diff; - -describe('ReactNativeAttributePayload', () => { - it('should work with simple example', () => { - expect(diff({a: 1, c: 3}, {b: 2, c: 3}, {a: true, b: true})).toEqual({ - a: null, - b: 2, - }); - }); - - it('should skip fields that are equal', () => { - expect( - diff( - {a: 1, b: 'two', c: true, d: false, e: undefined, f: 0}, - {a: 1, b: 'two', c: true, d: false, e: undefined, f: 0}, - {a: true, b: true, c: true, d: true, e: true, f: true}, - ), - ).toEqual(null); - }); - - it('should remove fields', () => { - expect(diff({a: 1}, {}, {a: true})).toEqual({a: null}); - }); - - it('should remove fields that are set to undefined', () => { - expect(diff({a: 1}, {a: undefined}, {a: true})).toEqual({a: null}); - }); - - it('should ignore invalid fields', () => { - expect(diff({a: 1}, {b: 2}, {})).toEqual(null); - }); - - it('should use the diff attribute', () => { - const diffA = jest.fn((a, b) => true); - const diffB = jest.fn((a, b) => false); - expect( - diff( - {a: [1], b: [3]}, - {a: [2], b: [4]}, - {a: {diff: diffA}, b: {diff: diffB}}, - ), - ).toEqual({a: [2]}); - expect(diffA).toBeCalledWith([1], [2]); - expect(diffB).toBeCalledWith([3], [4]); - }); - - it('should not use the diff attribute on addition/removal', () => { - const diffA = jest.fn(); - const diffB = jest.fn(); - expect( - diff({a: [1]}, {b: [2]}, {a: {diff: diffA}, b: {diff: diffB}}), - ).toEqual({a: null, b: [2]}); - expect(diffA).not.toBeCalled(); - expect(diffB).not.toBeCalled(); - }); - - it('should do deep diffs of Objects by default', () => { - expect( - diff( - {a: [1], b: {k: [3, 4]}, c: {k: [4, 4]}}, - {a: [2], b: {k: [3, 4]}, c: {k: [4, 5]}}, - {a: true, b: true, c: true}, - ), - ).toEqual({a: [2], c: {k: [4, 5]}}); - }); - - it('should work with undefined styles', () => { - expect( - diff( - {style: {a: '#ffffff', b: 1}}, - {style: undefined}, - {style: {b: true}}, - ), - ).toEqual({b: null}); - expect( - diff( - {style: undefined}, - {style: {a: '#ffffff', b: 1}}, - {style: {b: true}}, - ), - ).toEqual({b: 1}); - expect( - diff({style: undefined}, {style: undefined}, {style: {b: true}}), - ).toEqual(null); - }); - - it('should work with empty styles', () => { - expect(diff({a: 1, c: 3}, {}, {a: true, b: true})).toEqual({a: null}); - expect(diff({}, {a: 1, c: 3}, {a: true, b: true})).toEqual({a: 1}); - expect(diff({}, {}, {a: true, b: true})).toEqual(null); - }); - - it('should flatten nested styles and predefined styles', () => { - const validStyleAttribute = {someStyle: {foo: true, bar: true}}; - - expect( - diff({}, {someStyle: [{foo: 1}, {bar: 2}]}, validStyleAttribute), - ).toEqual({foo: 1, bar: 2}); - - expect( - diff({someStyle: [{foo: 1}, {bar: 2}]}, {}, validStyleAttribute), - ).toEqual({foo: null, bar: null}); - - const barStyle = { - bar: 3, - }; - - expect( - diff( - {}, - {someStyle: [[{foo: 1}, {foo: 2}], barStyle]}, - validStyleAttribute, - ), - ).toEqual({foo: 2, bar: 3}); - }); - - it('should reset a value to a previous if it is removed', () => { - const validStyleAttribute = {someStyle: {foo: true, bar: true}}; - - expect( - diff( - {someStyle: [{foo: 1}, {foo: 3}]}, - {someStyle: [{foo: 1}, {bar: 2}]}, - validStyleAttribute, - ), - ).toEqual({foo: 1, bar: 2}); - }); - - it('should not clear removed props if they are still in another slot', () => { - const validStyleAttribute = {someStyle: {foo: true, bar: true}}; - - expect( - diff( - {someStyle: [{}, {foo: 3, bar: 2}]}, - {someStyle: [{foo: 3}, {bar: 2}]}, - validStyleAttribute, - ), - ).toEqual({foo: 3}); // this should ideally be null. heuristic tradeoff. - - expect( - diff( - {someStyle: [{}, {foo: 3, bar: 2}]}, - {someStyle: [{foo: 1, bar: 1}, {bar: 2}]}, - validStyleAttribute, - ), - ).toEqual({bar: 2, foo: 1}); - }); - - it('should clear a prop if a later style is explicit null/undefined', () => { - const validStyleAttribute = {someStyle: {foo: true, bar: true}}; - expect( - diff( - {someStyle: [{}, {foo: 3, bar: 2}]}, - {someStyle: [{foo: 1}, {bar: 2, foo: null}]}, - validStyleAttribute, - ), - ).toEqual({foo: null}); - - expect( - diff( - {someStyle: [{foo: 3}, {foo: null, bar: 2}]}, - {someStyle: [{foo: null}, {bar: 2}]}, - validStyleAttribute, - ), - ).toEqual({foo: null}); - - expect( - diff( - {someStyle: [{foo: 1}, {foo: null}]}, - {someStyle: [{foo: 2}, {foo: null}]}, - validStyleAttribute, - ), - ).toEqual({foo: null}); // this should ideally be null. heuristic. - - // Test the same case with object equality because an early bailout doesn't - // work in this case. - const fooObj = {foo: 3}; - expect( - diff( - {someStyle: [{foo: 1}, fooObj]}, - {someStyle: [{foo: 2}, fooObj]}, - validStyleAttribute, - ), - ).toEqual({foo: 3}); // this should ideally be null. heuristic. - - expect( - diff( - {someStyle: [{foo: 1}, {foo: 3}]}, - {someStyle: [{foo: 2}, {foo: undefined}]}, - validStyleAttribute, - ), - ).toEqual({foo: null}); // this should ideally be null. heuristic. - }); - - it('handles attributes defined multiple times', () => { - const validAttributes = {foo: true, style: {foo: true}}; - expect(diff({}, {foo: 4, style: {foo: 2}}, validAttributes)).toEqual({ - foo: 2, - }); - expect(diff({foo: 4}, {style: {foo: 2}}, validAttributes)).toEqual({ - foo: 2, - }); - expect(diff({style: {foo: 2}}, {foo: 4}, validAttributes)).toEqual({ - foo: 4, - }); - }); - - // Function properties are just markers to native that events should be sent. - it('should convert functions to booleans', () => { - // Note that if the property changes from one function to another, we don't - // need to send an update. - expect( - diff( - { - a: function () { - return 1; - }, - b: function () { - return 2; - }, - c: 3, - }, - { - b: function () { - return 9; - }, - c: function () { - return 3; - }, - }, - {a: true, b: true, c: true}, - ), - ).toEqual({a: null, c: true}); - }); - - it('should skip changed functions', () => { - expect( - diff( - { - a: function () { - return 1; - }, - }, - { - a: function () { - return 9; - }, - }, - {a: true}, - ), - ).toEqual(null); - }); - - it('should skip deeply-nested changed functions', () => { - expect( - diff( - { - wrapper: { - a: function () { - return 1; - }, - }, - }, - { - wrapper: { - a: function () { - return 9; - }, - }, - }, - {wrapper: true}, - ), - ).toEqual(null); - }); -}); diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js deleted file mode 100644 index f4ab24d71d42..000000000000 --- a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js +++ /dev/null @@ -1,554 +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. - * - * @emails react-core - * @jest-environment node - */ - -'use strict'; - -let PropTypes; -let RCTEventEmitter; -let React; -let act; -let ReactNative; -let ResponderEventPlugin; -let UIManager; -let createReactNativeComponentClass; -let assertConsoleErrorDev; - -// Parallels requireNativeComponent() in that it lazily constructs a view config, -// And registers view manager event types with ReactNativeViewConfigRegistry. -const fakeRequireNativeComponent = (uiViewClassName, validAttributes) => { - const getViewConfig = () => { - const viewConfig = { - uiViewClassName, - validAttributes, - bubblingEventTypes: { - topTouchCancel: { - phasedRegistrationNames: { - bubbled: 'onTouchCancel', - captured: 'onTouchCancelCapture', - }, - }, - topTouchEnd: { - phasedRegistrationNames: { - bubbled: 'onTouchEnd', - captured: 'onTouchEndCapture', - }, - }, - topTouchMove: { - phasedRegistrationNames: { - bubbled: 'onTouchMove', - captured: 'onTouchMoveCapture', - }, - }, - topTouchStart: { - phasedRegistrationNames: { - bubbled: 'onTouchStart', - captured: 'onTouchStartCapture', - }, - }, - }, - directEventTypes: {}, - }; - - return viewConfig; - }; - - return createReactNativeComponentClass(uiViewClassName, getViewConfig); -}; - -beforeEach(() => { - jest.resetModules(); - - PropTypes = require('prop-types'); - RCTEventEmitter = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').RCTEventEmitter; - React = require('react'); - act = require('internal-test-utils').act; - assertConsoleErrorDev = require('internal-test-utils').assertConsoleErrorDev; - ReactNative = require('react-native-renderer'); - ResponderEventPlugin = - require('react-native-renderer/src/legacy-events/ResponderEventPlugin').default; - UIManager = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').UIManager; - createReactNativeComponentClass = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') - .ReactNativeViewConfigRegistry.register; -}); - -// @gate !disableLegacyMode -test('fails to register the same event name with different types', async () => { - const InvalidEvents = createReactNativeComponentClass('InvalidEvents', () => { - if (!__DEV__) { - // Simulate a registration error in prod. - throw new Error('Event cannot be both direct and bubbling: topChange'); - } - - // This view config has the same bubbling and direct event name - // which will fail to register in development. - return { - uiViewClassName: 'InvalidEvents', - validAttributes: { - onChange: true, - }, - bubblingEventTypes: { - topChange: { - phasedRegistrationNames: { - bubbled: 'onChange', - captured: 'onChangeCapture', - }, - }, - }, - directEventTypes: { - topChange: { - registrationName: 'onChange', - }, - }, - }; - }); - - // The first time this renders, - // we attempt to register the view config and fail. - await expect( - async () => await act(() => ReactNative.render(, 1)), - ).rejects.toThrow('Event cannot be both direct and bubbling: topChange'); - - // Continue to re-register the config and - // fail so that we don't mask the above failure. - await expect( - async () => await act(() => ReactNative.render(, 1)), - ).rejects.toThrow('Event cannot be both direct and bubbling: topChange'); -}); - -// @gate !disableLegacyMode -test('fails if unknown/unsupported event types are dispatched', () => { - expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1); - const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; - const View = fakeRequireNativeComponent('View', {}); - - ReactNative.render( {}} />, 1); - - expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(` - " {} - View null" - `); - expect(UIManager.createView).toHaveBeenCalledTimes(1); - - const target = UIManager.createView.mock.calls[0][0]; - - expect(() => { - EventEmitter.receiveTouches( - 'unspecifiedEvent', - [{target, identifier: 17}], - [0], - ); - }).toThrow('Unsupported top level event type "unspecifiedEvent" dispatched'); -}); - -// @gate !disableLegacyMode -test('handles events', () => { - expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1); - const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; - const View = fakeRequireNativeComponent('View', {foo: true}); - - const log = []; - ReactNative.render( - log.push('outer touchend')} - onTouchEndCapture={() => log.push('outer touchend capture')} - onTouchStart={() => log.push('outer touchstart')} - onTouchStartCapture={() => log.push('outer touchstart capture')}> - log.push('inner touchend capture')} - onTouchEnd={() => log.push('inner touchend')} - onTouchStartCapture={() => log.push('inner touchstart capture')} - onTouchStart={() => log.push('inner touchstart')} - /> - , - 1, - ); - - expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(` - " {} - View {"foo":"outer"} - View {"foo":"inner"}" - `); - expect(UIManager.createView).toHaveBeenCalledTimes(2); - - // Don't depend on the order of createView() calls. - // Stack creates views outside-in; fiber creates them inside-out. - const innerTag = UIManager.createView.mock.calls.find( - args => args[3].foo === 'inner', - )[0]; - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: innerTag, identifier: 17}], - [0], - ); - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: innerTag, identifier: 17}], - [0], - ); - - expect(log).toEqual([ - 'outer touchstart capture', - 'inner touchstart capture', - 'inner touchstart', - 'outer touchstart', - 'outer touchend capture', - 'inner touchend capture', - 'inner touchend', - 'outer touchend', - ]); -}); - -// @gate !disableLegacyContext || !__DEV__ -// @gate !disableLegacyMode -test('handles events on text nodes', () => { - expect(RCTEventEmitter.register).toHaveBeenCalledTimes(1); - const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; - const Text = fakeRequireNativeComponent('RCTText', {}); - - class ContextHack extends React.Component { - static childContextTypes = {isInAParentText: PropTypes.bool}; - getChildContext() { - return {isInAParentText: true}; - } - render() { - return this.props.children; - } - } - - const log = []; - ReactNative.render( - - - log.push('string touchend')} - onTouchEndCapture={() => log.push('string touchend capture')} - onTouchStart={() => log.push('string touchstart')} - onTouchStartCapture={() => log.push('string touchstart capture')}> - Text Content - - log.push('number touchend')} - onTouchEndCapture={() => log.push('number touchend capture')} - onTouchStart={() => log.push('number touchstart')} - onTouchStartCapture={() => log.push('number touchstart capture')}> - {123} - - - , - 1, - ); - assertConsoleErrorDev([ - 'ContextHack uses the legacy childContextTypes API which will soon be removed. ' + - 'Use React.createContext() instead. ' + - '(https://react.dev/link/legacy-context)' + - '\n in ContextHack (at **)', - ]); - - expect(UIManager.createView).toHaveBeenCalledTimes(5); - - // Don't depend on the order of createView() calls. - // Stack creates views outside-in; fiber creates them inside-out. - const innerTagString = UIManager.createView.mock.calls.find( - args => args[3] && args[3].text === 'Text Content', - )[0]; - const innerTagNumber = UIManager.createView.mock.calls.find( - args => args[3] && args[3].text === '123', - )[0]; - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: innerTagString, identifier: 17}], - [0], - ); - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: innerTagString, identifier: 17}], - [0], - ); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: innerTagNumber, identifier: 18}], - [0], - ); - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: innerTagNumber, identifier: 18}], - [0], - ); - - expect(log).toEqual([ - 'string touchstart capture', - 'string touchstart', - 'string touchend capture', - 'string touchend', - 'number touchstart capture', - 'number touchstart', - 'number touchend capture', - 'number touchend', - ]); -}); - -// @gate !disableLegacyMode -test('handles when a responder is unmounted while a touch sequence is in progress', () => { - const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; - const View = fakeRequireNativeComponent('View', {id: true}); - - function getViewById(id) { - return UIManager.createView.mock.calls.find( - args => args[3] && args[3].id === id, - )[0]; - } - - function getResponderId() { - const responder = ResponderEventPlugin._getResponder(); - if (responder === null) { - return null; - } - const props = responder.memoizedProps; - return props ? props.id : null; - } - - const log = []; - ReactNative.render( - - - log.push('one responder end')} - onResponderStart={() => log.push('one responder start')} - onStartShouldSetResponder={() => true} - /> - - - log.push('two responder end')} - onResponderStart={() => log.push('two responder start')} - onStartShouldSetResponder={() => true} - /> - - , - 1, - ); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: getViewById('one'), identifier: 17}], - [0], - ); - - expect(getResponderId()).toBe('one'); - expect(log).toEqual(['one responder start']); - log.splice(0); - - ReactNative.render( - - - log.push('two responder end')} - onResponderStart={() => log.push('two responder start')} - onStartShouldSetResponder={() => true} - /> - - , - 1, - ); - - // TODO Verify the onResponderEnd listener has been called (before the unmount) - // expect(log).toEqual(['one responder end']); - // log.splice(0); - - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: getViewById('two'), identifier: 17}], - [0], - ); - - expect(getResponderId()).toBeNull(); - expect(log).toEqual([]); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: getViewById('two'), identifier: 17}], - [0], - ); - - expect(getResponderId()).toBe('two'); - expect(log).toEqual(['two responder start']); -}); - -// @gate !disableLegacyMode -test('handles events without target', () => { - const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; - - const View = fakeRequireNativeComponent('View', {id: true}); - - function getViewById(id) { - return UIManager.createView.mock.calls.find( - args => args[3] && args[3].id === id, - )[0]; - } - - function getResponderId() { - const responder = ResponderEventPlugin._getResponder(); - if (responder === null) { - return null; - } - const props = responder.memoizedProps; - return props ? props.id : null; - } - - const log = []; - - function render(renderFirstComponent) { - ReactNative.render( - - - {renderFirstComponent ? ( - log.push('one responder end')} - onResponderStart={() => log.push('one responder start')} - onStartShouldSetResponder={() => true} - /> - ) : null} - - - log.push('two responder end')} - onResponderStart={() => log.push('two responder start')} - onStartShouldSetResponder={() => true} - /> - - , - 1, - ); - } - - render(true); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: getViewById('one'), identifier: 17}], - [0], - ); - - // Unmounting component 'one'. - render(false); - - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: getViewById('one'), identifier: 17}], - [0], - ); - - expect(getResponderId()).toBe(null); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: getViewById('two'), identifier: 18}], - [0], - ); - - expect(getResponderId()).toBe('two'); - - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: getViewById('two'), identifier: 18}], - [0], - ); - - expect(getResponderId()).toBe(null); - - expect(log).toEqual([ - 'one responder start', - 'two responder start', - 'two responder end', - ]); -}); - -// @gate !disableLegacyMode -test('dispatches event with target as instance', () => { - const EventEmitter = RCTEventEmitter.register.mock.calls[0][0]; - - const View = fakeRequireNativeComponent('View', {id: true}); - - function getViewById(id) { - return UIManager.createView.mock.calls.find( - args => args[3] && args[3].id === id, - )[0]; - } - - const ref1 = React.createRef(); - const ref2 = React.createRef(); - - ReactNative.render( - - { - expect(ref1.current).not.toBeNull(); - // Check for referential equality - expect(ref1.current).toBe(event.target); - expect(ref1.current).toBe(event.currentTarget); - }} - onStartShouldSetResponder={() => true} - /> - { - expect(ref2.current).not.toBeNull(); - // Check for referential equality - expect(ref2.current).toBe(event.target); - expect(ref2.current).toBe(event.currentTarget); - }} - onStartShouldSetResponder={() => true} - /> - , - 1, - ); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: getViewById('one'), identifier: 17}], - [0], - ); - - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: getViewById('one'), identifier: 17}], - [0], - ); - - EventEmitter.receiveTouches( - 'topTouchStart', - [{target: getViewById('two'), identifier: 18}], - [0], - ); - - EventEmitter.receiveTouches( - 'topTouchEnd', - [{target: getViewById('two'), identifier: 18}], - [0], - ); - - expect.assertions(6); -}); diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js deleted file mode 100644 index 0aa1c3f0ba0a..000000000000 --- a/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js +++ /dev/null @@ -1,771 +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. - * - * @emails react-core - * @jest-environment node - */ - -'use strict'; - -let React; -let StrictMode; -let ReactNative; -let createReactNativeComponentClass; -let UIManager; -let TextInputState; -let ReactNativePrivateInterface; -let act; -let assertConsoleErrorDev; - -const DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT = - "dispatchCommand was called with a ref that isn't a " + - 'native component. Use React.forwardRef to get access to the underlying native component'; - -const SEND_ACCESSIBILITY_EVENT_REQUIRES_HOST_COMPONENT = - "sendAccessibilityEvent was called with a ref that isn't a " + - 'native component. Use React.forwardRef to get access to the underlying native component'; - -describe('ReactNative', () => { - beforeEach(() => { - jest.resetModules(); - - React = require('react'); - ({act, assertConsoleErrorDev} = require('internal-test-utils')); - StrictMode = React.StrictMode; - ReactNative = require('react-native-renderer'); - ReactNativePrivateInterface = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'); - UIManager = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').UIManager; - createReactNativeComponentClass = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') - .ReactNativeViewConfigRegistry.register; - TextInputState = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface').TextInputState; - }); - - // @gate !disableLegacyMode - it('should be able to create and render a native component', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - ReactNative.render(, 1); - expect(UIManager.createView).toBeCalled(); - expect(UIManager.setChildren).toBeCalled(); - expect(UIManager.manageChildren).not.toBeCalled(); - expect(UIManager.updateView).not.toBeCalled(); - }); - - // @gate !disableLegacyMode - it('should be able to create and update a native component', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - ReactNative.render(, 11); - - expect(UIManager.createView).toHaveBeenCalledTimes(1); - expect(UIManager.setChildren).toHaveBeenCalledTimes(1); - expect(UIManager.manageChildren).not.toBeCalled(); - expect(UIManager.updateView).not.toBeCalled(); - - ReactNative.render(, 11); - - expect(UIManager.createView).toHaveBeenCalledTimes(1); - expect(UIManager.setChildren).toHaveBeenCalledTimes(1); - expect(UIManager.manageChildren).not.toBeCalled(); - expect(UIManager.updateView).toBeCalledWith(3, 'RCTView', {foo: 'bar'}); - }); - - // @gate !disableLegacyMode - it('should not call UIManager.updateView after render for properties that have not changed', () => { - const Text = createReactNativeComponentClass('RCTText', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTText', - })); - - ReactNative.render(1, 11); - expect(UIManager.updateView).not.toBeCalled(); - - // If no properties have changed, we shouldn't call updateView. - ReactNative.render(1, 11); - expect(UIManager.updateView).not.toBeCalled(); - - // Only call updateView for the changed property (and not for text). - ReactNative.render(1, 11); - expect(UIManager.updateView).toHaveBeenCalledTimes(1); - - // Only call updateView for the changed text (and no other properties). - ReactNative.render(2, 11); - expect(UIManager.updateView).toHaveBeenCalledTimes(2); - - // Call updateView for both changed text and properties. - ReactNative.render(3, 11); - expect(UIManager.updateView).toHaveBeenCalledTimes(4); - }); - - // @gate !disableLegacyMode - it('should call dispatchCommand for native refs', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - UIManager.dispatchViewManagerCommand.mockClear(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect(UIManager.dispatchViewManagerCommand).not.toBeCalled(); - ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]); - expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledTimes(1); - expect(UIManager.dispatchViewManagerCommand).toHaveBeenCalledWith( - expect.any(Number), - 'updateCommand', - [10, 20], - ); - }); - - // @gate !disableLegacyMode - it('should warn and no-op if calling dispatchCommand on non native refs', () => { - class BasicClass extends React.Component { - render() { - return ; - } - } - - UIManager.dispatchViewManagerCommand.mockReset(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect(UIManager.dispatchViewManagerCommand).not.toBeCalled(); - ReactNative.dispatchCommand(viewRef, 'updateCommand', [10, 20]); - assertConsoleErrorDev([DISPATCH_COMMAND_REQUIRES_HOST_COMPONENT], { - withoutStack: true, - }); - - expect(UIManager.dispatchViewManagerCommand).not.toBeCalled(); - }); - - // @gate !disableLegacyMode - it('should call sendAccessibilityEvent for native refs', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - ReactNativePrivateInterface.legacySendAccessibilityEvent.mockClear(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect( - ReactNativePrivateInterface.legacySendAccessibilityEvent, - ).not.toBeCalled(); - ReactNative.sendAccessibilityEvent(viewRef, 'focus'); - expect( - ReactNativePrivateInterface.legacySendAccessibilityEvent, - ).toHaveBeenCalledTimes(1); - expect( - ReactNativePrivateInterface.legacySendAccessibilityEvent, - ).toHaveBeenCalledWith(expect.any(Number), 'focus'); - }); - - // @gate !disableLegacyMode - it('should warn and no-op if calling sendAccessibilityEvent on non native refs', () => { - class BasicClass extends React.Component { - render() { - return ; - } - } - - UIManager.sendAccessibilityEvent.mockReset(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect(UIManager.sendAccessibilityEvent).not.toBeCalled(); - ReactNative.sendAccessibilityEvent(viewRef, 'updateCommand', [10, 20]); - assertConsoleErrorDev([SEND_ACCESSIBILITY_EVENT_REQUIRES_HOST_COMPONENT], { - withoutStack: true, - }); - - expect(UIManager.sendAccessibilityEvent).not.toBeCalled(); - }); - - // @gate !disableLegacyMode - it('should not call UIManager.updateView from ref.setNativeProps for properties that have not changed', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - UIManager.updateView.mockReset(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect(UIManager.updateView).not.toBeCalled(); - - viewRef.setNativeProps({}); - expect(UIManager.updateView).not.toBeCalled(); - - viewRef.setNativeProps({foo: 'baz'}); - expect(UIManager.updateView).toHaveBeenCalledTimes(1); - expect(UIManager.updateView).toHaveBeenCalledWith( - expect.any(Number), - 'RCTView', - {foo: 'baz'}, - ); - }); - - // @gate !disableLegacyMode - it('should call UIManager.measure on ref.measure', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - UIManager.measure.mockClear(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect(UIManager.measure).not.toBeCalled(); - const successCallback = jest.fn(); - viewRef.measure(successCallback); - expect(UIManager.measure).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100, 0, 0); - }); - - // @gate !disableLegacyMode - it('should call UIManager.measureInWindow on ref.measureInWindow', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - UIManager.measureInWindow.mockClear(); - - let viewRef; - ReactNative.render( - { - viewRef = ref; - }} - />, - 11, - ); - - expect(UIManager.measureInWindow).not.toBeCalled(); - const successCallback = jest.fn(); - viewRef.measureInWindow(successCallback); - expect(UIManager.measureInWindow).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledWith(10, 10, 100, 100); - }); - - // @gate !disableLegacyMode - it('should support reactTag in ref.measureLayout', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - UIManager.measureLayout.mockClear(); - - let viewRef; - let otherRef; - ReactNative.render( - - { - viewRef = ref; - }} - /> - { - otherRef = ref; - }} - /> - , - 11, - ); - - expect(UIManager.measureLayout).not.toBeCalled(); - const successCallback = jest.fn(); - const failureCallback = jest.fn(); - viewRef.measureLayout( - ReactNative.findNodeHandle(otherRef), - successCallback, - failureCallback, - ); - expect(UIManager.measureLayout).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100); - }); - - // @gate !disableLegacyMode - it('should support ref in ref.measureLayout of host components', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - UIManager.measureLayout.mockClear(); - - let viewRef; - let otherRef; - ReactNative.render( - - { - viewRef = ref; - }} - /> - { - otherRef = ref; - }} - /> - , - 11, - ); - - expect(UIManager.measureLayout).not.toBeCalled(); - const successCallback = jest.fn(); - const failureCallback = jest.fn(); - viewRef.measureLayout(otherRef, successCallback, failureCallback); - expect(UIManager.measureLayout).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledTimes(1); - expect(successCallback).toHaveBeenCalledWith(1, 1, 100, 100); - }); - - // @gate !disableLegacyMode - it('returns the correct instance and calls it in the callback', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - let a; - let b; - const c = ReactNative.render( - (a = v)} />, - 11, - function () { - b = this; - }, - ); - - expect(a).toBeTruthy(); - expect(a).toBe(b); - expect(a).toBe(c); - }); - - // @gate !disableLegacyMode - it('renders and reorders children', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {title: true}, - uiViewClassName: 'RCTView', - })); - - class Component extends React.Component { - render() { - const chars = this.props.chars.split(''); - return ( - - {chars.map(text => ( - - ))} - - ); - } - } - - // Mini multi-child stress test: lots of reorders, some adds, some removes. - const before = 'abcdefghijklmnopqrst'; - const after = 'mxhpgwfralkeoivcstzy'; - - ReactNative.render(, 11); - expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(` - " {} - RCTView null - RCTView {"title":"a"} - RCTView {"title":"b"} - RCTView {"title":"c"} - RCTView {"title":"d"} - RCTView {"title":"e"} - RCTView {"title":"f"} - RCTView {"title":"g"} - RCTView {"title":"h"} - RCTView {"title":"i"} - RCTView {"title":"j"} - RCTView {"title":"k"} - RCTView {"title":"l"} - RCTView {"title":"m"} - RCTView {"title":"n"} - RCTView {"title":"o"} - RCTView {"title":"p"} - RCTView {"title":"q"} - RCTView {"title":"r"} - RCTView {"title":"s"} - RCTView {"title":"t"}" - `); - - ReactNative.render(, 11); - expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(` - " {} - RCTView null - RCTView {"title":"m"} - RCTView {"title":"x"} - RCTView {"title":"h"} - RCTView {"title":"p"} - RCTView {"title":"g"} - RCTView {"title":"w"} - RCTView {"title":"f"} - RCTView {"title":"r"} - RCTView {"title":"a"} - RCTView {"title":"l"} - RCTView {"title":"k"} - RCTView {"title":"e"} - RCTView {"title":"o"} - RCTView {"title":"i"} - RCTView {"title":"v"} - RCTView {"title":"c"} - RCTView {"title":"s"} - RCTView {"title":"t"} - RCTView {"title":"z"} - RCTView {"title":"y"}" - `); - }); - - // @gate !disableLegacyMode - it('calls setState with no arguments', () => { - let mockArgs; - class Component extends React.Component { - componentDidMount() { - this.setState({}, (...args) => (mockArgs = args)); - } - render() { - return false; - } - } - - ReactNative.render(, 11); - expect(mockArgs.length).toEqual(0); - }); - - // @gate !disableLegacyMode - it('should not throw when is used inside of a ancestor', () => { - const Image = createReactNativeComponentClass('RCTImage', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTImage', - })); - const Text = createReactNativeComponentClass('RCTText', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTText', - })); - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTView', - })); - - ReactNative.render( - - - , - 11, - ); - - // Non-View things (e.g. Image) are fine - ReactNative.render( - - - , - 11, - ); - }); - - // @gate !disableLegacyMode - it('should throw for text not inside of a ancestor', async () => { - const ScrollView = createReactNativeComponentClass('RCTScrollView', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTScrollView', - })); - const Text = createReactNativeComponentClass('RCTText', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTText', - })); - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTView', - })); - - await expect(async () => { - await act(() => ReactNative.render(this should warn, 11)); - }).rejects.toThrow( - 'Text strings must be rendered within a component.', - ); - - await expect(async () => { - await act(() => - ReactNative.render( - - hi hello hi - , - 11, - ), - ); - }).rejects.toThrow( - 'Text strings must be rendered within a component.', - ); - }); - - // @gate !disableLegacyMode - it('should not throw for text inside of an indirect ancestor', () => { - const Text = createReactNativeComponentClass('RCTText', () => ({ - validAttributes: {}, - uiViewClassName: 'RCTText', - })); - - const Indirection = () => 'Hi'; - - ReactNative.render( - - - , - 11, - ); - }); - - // @gate !disableLegacyMode - it('findHostInstance_DEPRECATED should warn if used to find a host component inside StrictMode', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - let parent = undefined; - let child = undefined; - - class ContainsStrictModeChild extends React.Component { - render() { - return ( - - (child = n)} /> - - ); - } - } - - ReactNative.render( (parent = n)} />, 11); - - const match = ReactNative.findHostInstance_DEPRECATED(parent); - assertConsoleErrorDev([ - 'findHostInstance_DEPRECATED is deprecated in StrictMode. ' + - 'findHostInstance_DEPRECATED was passed an instance of ContainsStrictModeChild which renders StrictMode children. ' + - 'Instead, add a ref directly to the element you want to reference. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-find-node' + - '\n in RCTView (at **)' + - '\n in ContainsStrictModeChild (at **)', - ]); - expect(match).toBe(child); - }); - - // @gate !disableLegacyMode - it('findHostInstance_DEPRECATED should warn if passed a component that is inside StrictMode', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - let parent = undefined; - let child = undefined; - - class IsInStrictMode extends React.Component { - render() { - return (child = n)} />; - } - } - - ReactNative.render( - - (parent = n)} /> - , - 11, - ); - - const match = ReactNative.findHostInstance_DEPRECATED(parent); - assertConsoleErrorDev([ - 'findHostInstance_DEPRECATED is deprecated in StrictMode. ' + - 'findHostInstance_DEPRECATED was passed an instance of IsInStrictMode which is inside StrictMode. ' + - 'Instead, add a ref directly to the element you want to reference. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-find-node' + - '\n in RCTView (at **)' + - '\n in IsInStrictMode (at **)', - ]); - expect(match).toBe(child); - }); - - // @gate !disableLegacyMode - it('findNodeHandle should warn if used to find a host component inside StrictMode', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - let parent = undefined; - let child = undefined; - - class ContainsStrictModeChild extends React.Component { - render() { - return ( - - (child = n)} /> - - ); - } - } - - ReactNative.render( (parent = n)} />, 11); - - const match = ReactNative.findNodeHandle(parent); - assertConsoleErrorDev([ - 'findNodeHandle is deprecated in StrictMode. ' + - 'findNodeHandle was passed an instance of ContainsStrictModeChild which renders StrictMode children. ' + - 'Instead, add a ref directly to the element you want to reference. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-find-node' + - '\n in RCTView (at **)' + - '\n in ContainsStrictModeChild (at **)', - ]); - expect(match).toBe(child._nativeTag); - }); - - // @gate !disableLegacyMode - it('findNodeHandle should warn if passed a component that is inside StrictMode', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - let parent = undefined; - let child = undefined; - - class IsInStrictMode extends React.Component { - render() { - return (child = n)} />; - } - } - - ReactNative.render( - - (parent = n)} /> - , - 11, - ); - - const match = ReactNative.findNodeHandle(parent); - assertConsoleErrorDev([ - 'findNodeHandle is deprecated in StrictMode. ' + - 'findNodeHandle was passed an instance of IsInStrictMode which is inside StrictMode. ' + - 'Instead, add a ref directly to the element you want to reference. ' + - 'Learn more about using refs safely here: ' + - 'https://react.dev/link/strict-mode-find-node' + - '\n in RCTView (at **)' + - '\n in IsInStrictMode (at **)', - ]); - expect(match).toBe(child._nativeTag); - }); - - // @gate !disableLegacyMode - it('blur on host component calls TextInputState', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - const viewRef = React.createRef(); - ReactNative.render(, 11); - - expect(TextInputState.blurTextInput).not.toBeCalled(); - - viewRef.current.blur(); - - expect(TextInputState.blurTextInput).toHaveBeenCalledTimes(1); - expect(TextInputState.blurTextInput).toHaveBeenCalledWith(viewRef.current); - }); - - // @gate !disableLegacyMode - it('focus on host component calls TextInputState', () => { - const View = createReactNativeComponentClass('RCTView', () => ({ - validAttributes: {foo: true}, - uiViewClassName: 'RCTView', - })); - - const viewRef = React.createRef(); - ReactNative.render(, 11); - - expect(TextInputState.focusTextInput).not.toBeCalled(); - - viewRef.current.focus(); - - expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1); - expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current); - }); -}); diff --git a/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js b/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js deleted file mode 100644 index 82199fd79e64..000000000000 --- a/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js +++ /dev/null @@ -1,76 +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. - * - * @emails react-core - * @jest-environment node - */ - -'use strict'; - -let createReactNativeComponentClass; -let React; -let ReactNative; - -describe('createReactNativeComponentClass', () => { - beforeEach(() => { - jest.resetModules(); - - createReactNativeComponentClass = - require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface') - .ReactNativeViewConfigRegistry.register; - React = require('react'); - ReactNative = require('react-native-renderer'); - }); - - // @gate !disableLegacyMode - it('should register viewConfigs', () => { - const textViewConfig = { - validAttributes: {}, - uiViewClassName: 'Text', - }; - const viewViewConfig = { - validAttributes: {}, - uiViewClassName: 'View', - }; - - const Text = createReactNativeComponentClass( - textViewConfig.uiViewClassName, - () => textViewConfig, - ); - const View = createReactNativeComponentClass( - viewViewConfig.uiViewClassName, - () => viewViewConfig, - ); - - expect(Text).not.toBe(View); - - ReactNative.render(, 1); - ReactNative.render(, 1); - }); - - it('should not allow viewConfigs with duplicate uiViewClassNames to be registered', () => { - const textViewConfig = { - validAttributes: {}, - uiViewClassName: 'Text', - }; - const altTextViewConfig = { - validAttributes: {}, - uiViewClassName: 'Text', // Same - }; - - createReactNativeComponentClass( - textViewConfig.uiViewClassName, - () => textViewConfig, - ); - - expect(() => { - createReactNativeComponentClass( - altTextViewConfig.uiViewClassName, - () => altTextViewConfig, - ); - }).toThrow('Tried to register two views with the same name Text'); - }); -}); diff --git a/packages/react-reconciler/src/forks/ReactFiberConfig.native.js b/packages/react-reconciler/src/forks/ReactFiberConfig.native.js deleted file mode 100644 index 3e06abc660ef..000000000000 --- a/packages/react-reconciler/src/forks/ReactFiberConfig.native.js +++ /dev/null @@ -1,11 +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 - */ - -export * from 'react-native-renderer/src/ReactFiberConfigNative'; -export * from 'react-client/src/ReactClientConsoleConfigPlain'; diff --git a/packages/react/src/__tests__/ReactMismatchedVersions-test.js b/packages/react/src/__tests__/ReactMismatchedVersions-test.js index d815f298641a..00a9abe98986 100644 --- a/packages/react/src/__tests__/ReactMismatchedVersions-test.js +++ b/packages/react/src/__tests__/ReactMismatchedVersions-test.js @@ -136,14 +136,4 @@ describe('ReactMismatchedVersions-test', () => { ` - react-dom: ${actualReactVersion}`, ); }); - - // @gate source - it('importing "react-native-renderer" throws if version does not match React version', async () => { - expect(() => require('react-native-renderer')).toThrow( - 'Incompatible React versions: The "react" and "react-native-renderer" packages ' + - 'must have the exact same version. Instead got:\n' + - ' - react: 18.0.0-whoa-this-aint-the-right-react\n' + - ` - react-native-renderer: ${actualReactVersion}`, - ); - }); }); diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js index b09d3d42531e..7fd039cb01e5 100644 --- a/scripts/flow/react-native-host-hooks.js +++ b/scripts/flow/react-native-host-hooks.js @@ -42,7 +42,6 @@ type __RNTopLevelEventType = any; // from 'react-reconciler/src/ReactCapturedValue' type __CapturedError = any; -type DeepDifferOptions = {+unsafelyIgnoreFunctions?: boolean}; type RawEventEmitterEvent = $ReadOnly<{ eventName: string, // We expect, but do not/cannot require, that nativeEvent is an object @@ -56,101 +55,11 @@ declare opaque type __PublicTextInstance; declare opaque type __PublicRootInstance; declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface' { - declare export function deepDiffer( - one: any, - two: any, - maxDepth?: number, - options?: DeepDifferOptions, - ): boolean; - declare export function deepDiffer( - one: any, - two: any, - options: DeepDifferOptions, - ): boolean; declare export function deepFreezeAndThrowOnMutationInDev(obj: T): T; - declare export function flattenStyle(style: any): any; - declare export const RCTEventEmitter: { - register: (eventEmitter: mixed) => void, - ... - }; - declare export const TextInputState: { - blurTextInput: (object: any) => void, - focusTextInput: (object: any) => void, - ... - }; declare export const ReactFiberErrorDialog: { showErrorDialog: (error: __CapturedError) => boolean, ... }; - declare export const Platform: {OS: string, ...}; - declare export const UIManager: { - customBubblingEventTypes: Object, - customDirectEventTypes: Object, - createView: ( - reactTag: number, - viewName: string, - rootTag: number, - props: ?Object, - ) => void, - dispatchViewManagerCommand: ( - reactTag: number, - command: string, - args: Array, - ) => void, - manageChildren: ( - containerTag: number, - moveFromIndices: Array, - moveToIndices: Array, - addChildReactTags: Array, - addAtIndices: Array, - removeAtIndices: Array, - ) => void, - measure: (hostComponent: mixed, callback: Function) => void, - measureInWindow: (nativeTag: ?number, callback: Function) => void, - measureLayout: ( - nativeTag: mixed, - nativeNode: number, - onFail: Function, - onSuccess: Function, - ) => void, - removeRootView: (containerTag: number) => void, - removeSubviewsFromContainerWithID: (containerId: number) => void, - replaceExistingNonRootView: () => void, - setChildren: (containerTag: number, reactTags: Array) => void, - updateView: (reactTag: number, viewName: string, props: ?Object) => void, - __takeSnapshot: ( - view?: 'window' | Element | number, - options?: { - width?: number, - height?: number, - format?: 'png' | 'jpeg', - quality?: number, - ... - }, - ) => Promise, - setJSResponder: (reactTag: number, blockNativeResponder: boolean) => void, - clearJSResponder: () => void, - findSubviewIn: ( - reactTag: ?number, - point: Array, - callback: ( - nativeViewTag: number, - left: number, - top: number, - width: number, - height: number, - ) => void, - ) => void, - ... - }; - declare export const legacySendAccessibilityEvent: ( - reactTag: number, - eventTypeName: string, - ) => void; - declare export const BatchedBridge: { - registerCallableModule: (name: string, module: Object) => void, - ... - }; declare export const ReactNativeViewConfigRegistry: { customBubblingEventTypes: Object, customDirectEventTypes: Object, @@ -171,18 +80,6 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface' __MeasureInWindowOnSuccessCallback; declare export type MeasureLayoutOnSuccessCallback = __MeasureLayoutOnSuccessCallback; - declare export interface LegacyPublicInstance { - blur(): void; - focus(): void; - measure(callback: __MeasureOnSuccessCallback): void; - measureInWindow(callback: __MeasureInWindowOnSuccessCallback): void; - measureLayout( - relativeToNativeNode: number | LegacyPublicInstance, - onSuccess: __MeasureLayoutOnSuccessCallback, - onFail?: () => void, - ): void; - setNativeProps(nativeProps: {...}): void; - } declare export function getNodeFromPublicInstance( publicInstance: PublicInstance, ): Object; diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index 2120b370ad34..df2123283d6b 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -133,21 +133,6 @@ const forks = Object.freeze({ // We have a few forks for different environments. './packages/shared/ReactFeatureFlags.js': (bundleType, entry) => { switch (entry) { - case 'react-native-renderer': - switch (bundleType) { - case RN_FB_DEV: - case RN_FB_PROD: - case RN_FB_PROFILING: - return './packages/shared/forks/ReactFeatureFlags.native-fb.js'; - case RN_OSS_DEV: - case RN_OSS_PROD: - case RN_OSS_PROFILING: - return './packages/shared/forks/ReactFeatureFlags.native-oss.js'; - default: - throw Error( - `Unexpected entry (${entry}) and bundleType (${bundleType})` - ); - } case 'react-native-renderer/fabric': switch (bundleType) { case RN_FB_DEV: diff --git a/scripts/rollup/shims/react-native/ReactNative.js b/scripts/rollup/shims/react-native/ReactNative.js deleted file mode 100644 index 4e7ab19ae6e6..000000000000 --- a/scripts/rollup/shims/react-native/ReactNative.js +++ /dev/null @@ -1,24 +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. - * - * @noformat - * @nolint - * @flow - */ -'use strict'; - -import type {ReactNativeType} from './ReactNativeTypes'; - -let ReactNative: ReactNativeType; - -// TODO: Delete the legacy renderer. Only ReactFabric is used now. -if (__DEV__) { - ReactNative = require('../implementations/ReactNativeRenderer-dev'); -} else { - ReactNative = require('../implementations/ReactNativeRenderer-prod'); -} - -export default ReactNative; diff --git a/scripts/shared/inlinedHostConfigs.js b/scripts/shared/inlinedHostConfigs.js index 0135dda88767..631c6585f31b 100644 --- a/scripts/shared/inlinedHostConfigs.js +++ b/scripts/shared/inlinedHostConfigs.js @@ -621,16 +621,6 @@ module.exports = [ isServerSupported: true, isFlightSupported: false, }, - { - shortName: 'native', - entryPoints: ['react-native-renderer'], - paths: [ - 'react-native-renderer', - 'react-server/src/ReactFlightServerConfigDebugNoop.js', - ], - isFlowTyped: true, - isServerSupported: false, - }, { shortName: 'fabric', entryPoints: ['react-native-renderer/fabric'],