Files
Rubén Norte 9dd378ff12 [RN] Move definition of public instances to ReactNativePrivateInterface (#32446)
## Summary

> [!NOTE]
> This only modifies types, so shouldn't have an impact at runtime.

Some time ago we moved some type definitions from React to React Native
in #26437.

This continues making progress on that so values that are created by
React Native and passed to the React renderer (in this case public
instances) are actually defined in React Native and not in React.

This will allow us to modify the definition of some of these types
without having to make changes in the React repository (in the short
term, we want to refactor PublicInstance from an object to an interface,
and then modify that interface to add all the new DOM methods).

## How did you test this change?

Manually synced `ReactNativeTypes` on top of
https://github.com/facebook/react-native/pull/49602 and verified Flow
passes.
2025-02-24 13:46:06 +00:00

273 lines
8.6 KiB
JavaScript

/**
* 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 {Node} from './ReactNativeTypes';
import type {ElementRef, ElementType} from 'react';
import type {PublicInstance} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
// Modules provided by RN:
import {
UIManager,
legacySendAccessibilityEvent,
getNodeFromPublicInstance,
getNativeTagFromPublicInstance,
getInternalInstanceHandleFromPublicInstance,
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import {
findHostInstance,
findHostInstanceWithWarning,
} from 'react-reconciler/src/ReactFiberReconciler';
import {doesFiberContain} from 'react-reconciler/src/ReactFiberTreeReflection';
import getComponentNameFromType from 'shared/getComponentNameFromType';
import {
current as currentOwner,
isRendering,
} from 'react-reconciler/src/ReactCurrentFiber';
export function findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?PublicInstance {
if (__DEV__) {
const owner = currentOwner;
if (owner !== null && isRendering && owner.stateNode !== null) {
if (!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 = true;
}
}
if (componentOrHandle == null) {
return null;
}
// For compatibility with Fabric instances
if (
componentOrHandle.canonical &&
componentOrHandle.canonical.publicInstance
) {
// $FlowExpectedError[incompatible-return] Can't refine componentOrHandle as a Fabric instance
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(
componentOrHandle,
'findHostInstance_DEPRECATED',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}
// findHostInstance handles legacy vs. Fabric differences correctly
// $FlowFixMe[incompatible-exact] we need to fix the definition of `HostComponent` to use NativeMethods as an interface, not as a type.
// $FlowFixMe[incompatible-return]
return hostInstance;
}
export function findNodeHandle(componentOrHandle: any): ?number {
if (__DEV__) {
const owner = currentOwner;
if (owner !== null && isRendering && owner.stateNode !== null) {
if (!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 = true;
}
}
if (componentOrHandle == null) {
return null;
}
if (typeof componentOrHandle === 'number') {
// Already a node handle
return componentOrHandle;
}
// For compatibility with legacy renderer instances
if (componentOrHandle._nativeTag) {
return componentOrHandle._nativeTag;
}
// For compatibility with Fabric instances
if (
componentOrHandle.canonical != null &&
componentOrHandle.canonical.nativeTag != null
) {
return componentOrHandle.canonical.nativeTag;
}
// For compatibility with Fabric public instances
const nativeTag = getNativeTagFromPublicInstance(componentOrHandle);
if (nativeTag) {
return nativeTag;
}
let hostInstance;
if (__DEV__) {
hostInstance = findHostInstanceWithWarning(
componentOrHandle,
'findNodeHandle',
);
} else {
hostInstance = findHostInstance(componentOrHandle);
}
if (hostInstance == null) {
// $FlowFixMe[incompatible-return] Flow limitation in refining an opaque type
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);
}
export function dispatchCommand(
handle: any,
command: string,
args: Array<any>,
) {
const nativeTag =
handle._nativeTag != null
? handle._nativeTag
: getNativeTagFromPublicInstance(handle);
if (nativeTag == null) {
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) {
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);
}
}
export function getNodeFromInternalInstanceHandle(
internalInstanceHandle: mixed,
): ?Node {
return (
// $FlowExpectedError[incompatible-return] internalInstanceHandle is opaque but we need to make an exception here.
internalInstanceHandle &&
// $FlowExpectedError[incompatible-return]
internalInstanceHandle.stateNode &&
// $FlowExpectedError[incompatible-use]
internalInstanceHandle.stateNode.node
);
}
// 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);
const childInternalInstanceHandle =
// $FlowExpectedError[incompatible-call] Type for childInstance should have been PublicInstance from ReactFiberConfigFabric.
getInternalInstanceHandleFromPublicInstance(childInstance);
// Fabric
if (
parentInternalInstanceHandle != null &&
childInternalInstanceHandle != null
) {
return doesFiberContain(
parentInternalInstanceHandle,
childInternalInstanceHandle,
);
}
// Means that one instance is from Fabric and other is from Paper.
return false;
} else {
throw new Error('isChildPublicInstance() is not available in production.');
}
}