[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.

<details>
<summary>diff</summary> 

```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);
```

</details>
This commit is contained in:
Rubén Norte
2026-04-16 18:34:03 +01:00
committed by GitHub
parent 60de2d11a2
commit 273365041c
35 changed files with 18 additions and 4705 deletions
-16
View File
@@ -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';
@@ -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,
);
}
}
}
}
@@ -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;
@@ -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 <Text> 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<TransitionStatus> = {
$$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 {}
@@ -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<NestedNode> | 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<NestedNode>,
nextArray: Array<NestedNode>,
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,
);
}
@@ -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);
}
@@ -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<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices by which to pull subsequence.
* @return {Array<Touch>} 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<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
function removeTouchesAtIndices(
touches: Array<Object>,
indices: Array<number>,
): Array<Object> {
const rippedOut = [];
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
const temp: Array<?Object> = (touches: Array<any>);
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> | ReactSyntheticEvent | null {
let events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null = null;
const legacyPlugins = ((plugins: any): Array<
LegacyPluginModule<AnyNativeEvent>,
>);
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<Object>,
changedIndices: Array<number>,
) {
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);
}
}
@@ -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<Instance | number>;
_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;
@@ -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};
@@ -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;
@@ -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,
);
+10 -66
View File
@@ -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<TElementType: ElementType>(
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<any>,
) {
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;
}
const node = getNodeFromPublicInstance(handle);
if (node != null) {
nativeFabricUIManager.dispatchCommand(node, command, args);
} 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) {
const node = getNodeFromPublicInstance(handle);
if (node != null) {
nativeFabricUIManager.sendAccessibilityEvent(node, eventType);
} else {
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.');
@@ -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<ElementType> {
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<number, FiberRoot>();
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();
-30
View File
@@ -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<TElementType: React.ElementType>(
componentOrHandle: ?(React.ElementRef<TElementType> | number),
): ?PublicInstance,
findNodeHandle<TElementType: React.ElementType>(
componentOrHandle: ?(React.ElementRef<TElementType> | number),
): ?number,
isChildPublicInstance(parent: PublicInstance, child: PublicInstance): boolean,
dispatchCommand(
handle: PublicInstance,
command: string,
args: Array<mixed>,
): void,
sendAccessibilityEvent(handle: PublicInstance, eventType: string): void,
render(
element: React.MixedElement,
containerTag: number,
callback: ?() => void,
options: ?RenderRootOptions,
): ?React.ElementRef<React.ElementType>,
unmountComponentAtNode(containerTag: number): void,
unmountComponentAtNodeAndRemoveContainer(containerTag: number): void,
+unstable_batchedUpdates: <T>(fn: (T) => void, bookkeeping: T) => void,
...
};
export opaque type Node = mixed;
export opaque type InternalInstanceHandle = mixed;
@@ -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;
@@ -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(),
};
@@ -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',
};
@@ -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;
@@ -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;
},
@@ -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;
@@ -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: '<native root>',
});
}
}
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;
@@ -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;
@@ -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();
@@ -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();
});
@@ -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 <View title="foo" />;
}
}
ReactFabric.render(<Component ref={ref} />, 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 <View title="foo" />;
}
}
ReactFabric.render(<Component ref={ref} />, 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(<View title="bar" ref={ref} />, 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(<View title="bar" ref={ref} />, 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 <View title="foo" />;
}
}
ReactNative.render(<Component ref={ref} />, 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 <View title="foo" />;
}
}
ReactNative.render(<Component ref={ref} />, 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(<View title="bar" ref={ref} />, 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(<View title="bar" ref={ref} />, 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();
});
});
@@ -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);
});
});
@@ -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(<InvalidEvents />, 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(<InvalidEvents />, 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(<View onUnspecifiedEvent={() => {}} />, 1);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(`
"<native root> {}
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(
<View
foo="outer"
onTouchEnd={() => log.push('outer touchend')}
onTouchEndCapture={() => log.push('outer touchend capture')}
onTouchStart={() => log.push('outer touchstart')}
onTouchStartCapture={() => log.push('outer touchstart capture')}>
<View
foo="inner"
onTouchEndCapture={() => log.push('inner touchend capture')}
onTouchEnd={() => log.push('inner touchend')}
onTouchStartCapture={() => log.push('inner touchstart capture')}
onTouchStart={() => log.push('inner touchstart')}
/>
</View>,
1,
);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(`
"<native root> {}
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(
<ContextHack>
<Text>
<Text
onTouchEnd={() => log.push('string touchend')}
onTouchEndCapture={() => log.push('string touchend capture')}
onTouchStart={() => log.push('string touchstart')}
onTouchStartCapture={() => log.push('string touchstart capture')}>
Text Content
</Text>
<Text
onTouchEnd={() => log.push('number touchend')}
onTouchEndCapture={() => log.push('number touchend capture')}
onTouchStart={() => log.push('number touchstart')}
onTouchStartCapture={() => log.push('number touchstart capture')}>
{123}
</Text>
</Text>
</ContextHack>,
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(
<View id="parent">
<View key={1}>
<View
id="one"
onResponderEnd={() => log.push('one responder end')}
onResponderStart={() => log.push('one responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
<View key={2}>
<View
id="two"
onResponderEnd={() => log.push('two responder end')}
onResponderStart={() => log.push('two responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
</View>,
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(
<View id="parent">
<View key={2}>
<View
id="two"
onResponderEnd={() => log.push('two responder end')}
onResponderStart={() => log.push('two responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
</View>,
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(
<View id="parent">
<View key={1}>
{renderFirstComponent ? (
<View
id="one"
onResponderEnd={() => log.push('one responder end')}
onResponderStart={() => log.push('one responder start')}
onStartShouldSetResponder={() => true}
/>
) : null}
</View>
<View key={2}>
<View
id="two"
onResponderEnd={() => log.push('two responder end')}
onResponderStart={() => log.push('two responder start')}
onStartShouldSetResponder={() => true}
/>
</View>
</View>,
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(
<View id="parent">
<View
ref={ref1}
id="one"
onResponderStart={event => {
expect(ref1.current).not.toBeNull();
// Check for referential equality
expect(ref1.current).toBe(event.target);
expect(ref1.current).toBe(event.currentTarget);
}}
onStartShouldSetResponder={() => true}
/>
<View
ref={ref2}
id="two"
onResponderStart={event => {
expect(ref2.current).not.toBeNull();
// Check for referential equality
expect(ref2.current).toBe(event.target);
expect(ref2.current).toBe(event.currentTarget);
}}
onStartShouldSetResponder={() => true}
/>
</View>,
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);
});
@@ -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(<View foo="test" />, 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(<View foo="foo" />, 11);
expect(UIManager.createView).toHaveBeenCalledTimes(1);
expect(UIManager.setChildren).toHaveBeenCalledTimes(1);
expect(UIManager.manageChildren).not.toBeCalled();
expect(UIManager.updateView).not.toBeCalled();
ReactNative.render(<View foo="bar" />, 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(<Text foo="a">1</Text>, 11);
expect(UIManager.updateView).not.toBeCalled();
// If no properties have changed, we shouldn't call updateView.
ReactNative.render(<Text foo="a">1</Text>, 11);
expect(UIManager.updateView).not.toBeCalled();
// Only call updateView for the changed property (and not for text).
ReactNative.render(<Text foo="b">1</Text>, 11);
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
// Only call updateView for the changed text (and no other properties).
ReactNative.render(<Text foo="b">2</Text>, 11);
expect(UIManager.updateView).toHaveBeenCalledTimes(2);
// Call updateView for both changed text and properties.
ReactNative.render(<Text foo="c">3</Text>, 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(
<View
ref={ref => {
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 <React.Fragment />;
}
}
UIManager.dispatchViewManagerCommand.mockReset();
let viewRef;
ReactNative.render(
<BasicClass
ref={ref => {
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(
<View
ref={ref => {
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 <React.Fragment />;
}
}
UIManager.sendAccessibilityEvent.mockReset();
let viewRef;
ReactNative.render(
<BasicClass
ref={ref => {
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(
<View
foo="bar"
ref={ref => {
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(
<View
ref={ref => {
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(
<View
ref={ref => {
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(
<View>
<View
foo="bar"
ref={ref => {
viewRef = ref;
}}
/>
<View
ref={ref => {
otherRef = ref;
}}
/>
</View>,
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(
<View>
<View
foo="bar"
ref={ref => {
viewRef = ref;
}}
/>
<View
ref={ref => {
otherRef = ref;
}}
/>
</View>,
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(
<View foo="foo" ref={v => (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 (
<View>
{chars.map(text => (
<View key={text} title={text} />
))}
</View>
);
}
}
// Mini multi-child stress test: lots of reorders, some adds, some removes.
const before = 'abcdefghijklmnopqrst';
const after = 'mxhpgwfralkeoivcstzy';
ReactNative.render(<Component chars={before} />, 11);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(`
"<native root> {}
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(<Component chars={after} />, 11);
expect(UIManager.__dumpHierarchyForJestTestsOnly()).toMatchInlineSnapshot(`
"<native root> {}
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(<Component />, 11);
expect(mockArgs.length).toEqual(0);
});
// @gate !disableLegacyMode
it('should not throw when <View> is used inside of a <Text> ancestor', () => {
const Image = createReactNativeComponentClass('RCTImage', () => ({
validAttributes: {},
uiViewClassName: 'RCTImage',
}));
const Text = createReactNativeComponentClass('RCTText', () => ({
validAttributes: {},
uiViewClassName: 'RCTText',
}));
const View = createReactNativeComponentClass('RCTView', () => ({
validAttributes: {},
uiViewClassName: 'RCTView',
}));
ReactNative.render(
<Text>
<View />
</Text>,
11,
);
// Non-View things (e.g. Image) are fine
ReactNative.render(
<Text>
<Image />
</Text>,
11,
);
});
// @gate !disableLegacyMode
it('should throw for text not inside of a <Text> 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(<View>this should warn</View>, 11));
}).rejects.toThrow(
'Text strings must be rendered within a <Text> component.',
);
await expect(async () => {
await act(() =>
ReactNative.render(
<Text>
<ScrollView>hi hello hi</ScrollView>
</Text>,
11,
),
);
}).rejects.toThrow(
'Text strings must be rendered within a <Text> component.',
);
});
// @gate !disableLegacyMode
it('should not throw for text inside of an indirect <Text> ancestor', () => {
const Text = createReactNativeComponentClass('RCTText', () => ({
validAttributes: {},
uiViewClassName: 'RCTText',
}));
const Indirection = () => 'Hi';
ReactNative.render(
<Text>
<Indirection />
</Text>,
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 (
<StrictMode>
<View ref={n => (child = n)} />
</StrictMode>
);
}
}
ReactNative.render(<ContainsStrictModeChild ref={n => (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 <View ref={n => (child = n)} />;
}
}
ReactNative.render(
<StrictMode>
<IsInStrictMode ref={n => (parent = n)} />
</StrictMode>,
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 (
<StrictMode>
<View ref={n => (child = n)} />
</StrictMode>
);
}
}
ReactNative.render(<ContainsStrictModeChild ref={n => (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 <View ref={n => (child = n)} />;
}
}
ReactNative.render(
<StrictMode>
<IsInStrictMode ref={n => (parent = n)} />
</StrictMode>,
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(<View ref={viewRef} />, 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(<View ref={viewRef} />, 11);
expect(TextInputState.focusTextInput).not.toBeCalled();
viewRef.current.focus();
expect(TextInputState.focusTextInput).toHaveBeenCalledTimes(1);
expect(TextInputState.focusTextInput).toHaveBeenCalledWith(viewRef.current);
});
});
@@ -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(<Text />, 1);
ReactNative.render(<View />, 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');
});
});
@@ -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';
@@ -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}`,
);
});
});
-103
View File
@@ -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<T>(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<any>,
) => void,
manageChildren: (
containerTag: number,
moveFromIndices: Array<number>,
moveToIndices: Array<number>,
addChildReactTags: Array<number>,
addAtIndices: Array<number>,
removeAtIndices: Array<number>,
) => 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<number>) => 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<any>,
setJSResponder: (reactTag: number, blockNativeResponder: boolean) => void,
clearJSResponder: () => void,
findSubviewIn: (
reactTag: ?number,
point: Array<number>,
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;
-15
View File
@@ -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:
-24
View File
@@ -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;
-10
View File
@@ -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'],