mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[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:
-16
@@ -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
@@ -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();
|
||||
@@ -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;
|
||||
|
||||
|
||||
Vendored
-14
@@ -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;
|
||||
-12
@@ -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(),
|
||||
};
|
||||
Vendored
-11
@@ -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',
|
||||
};
|
||||
Vendored
-14
@@ -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;
|
||||
-24
@@ -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;
|
||||
},
|
||||
|
||||
Vendored
-18
@@ -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;
|
||||
Vendored
-194
@@ -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;
|
||||
Vendored
-14
@@ -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;
|
||||
-10
@@ -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();
|
||||
});
|
||||
});
|
||||
-287
@@ -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);
|
||||
});
|
||||
});
|
||||
-76
@@ -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
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
@@ -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'],
|
||||
|
||||
Reference in New Issue
Block a user