mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
9dd378ff12
## 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.
273 lines
8.6 KiB
JavaScript
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.');
|
|
}
|
|
}
|