/** * 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, Element, ElementType} from 'react'; import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes'; import './ReactFabricInjection'; import { batchedUpdates as batchedUpdatesImpl, discreteUpdates, createContainer, updateContainer, injectIntoDevTools, getPublicRootInstance, defaultOnUncaughtError, defaultOnCaughtError, defaultOnRecoverableError, } from 'react-reconciler/src/ReactFiberReconciler'; import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal'; import {setBatchingImplementation} from './legacy-events/ReactGenericBatching'; import ReactVersion from 'shared/ReactVersion'; import {getClosestInstanceFromNode} from './ReactFabricComponentTree'; import { getInspectorDataForViewTag, getInspectorDataForViewAtPoint, getInspectorDataForInstance, } from './ReactNativeFiberInspector'; import {LegacyRoot, ConcurrentRoot} from 'react-reconciler/src/ReactRootTags'; import { findHostInstance_DEPRECATED, findNodeHandle, dispatchCommand, sendAccessibilityEvent, getNodeFromInternalInstanceHandle, isChildPublicInstance, } from './ReactNativePublicCompat'; import {getPublicInstanceFromInternalInstanceHandle} from './ReactFiberConfigFabric'; // Module provided by RN: import {ReactFiberErrorDialog} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'; 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?: ?React$Component, }, ): 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 render( element: Element, containerTag: number, callback: ?() => void, concurrentRoot: ?boolean, ): ?ElementRef { let root = roots.get(containerTag); if (!root) { // 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( containerTag, concurrentRoot ? ConcurrentRoot : LegacyRoot, null, false, null, '', nativeOnUncaughtError, nativeOnCaughtError, defaultOnRecoverableError, null, ); roots.set(containerTag, root); } updateContainer(element, root, null, callback); return getPublicRootInstance(root); } // $FlowFixMe[missing-this-annot] function unmountComponentAtNode(containerTag: number) { this.stopSurface(containerTag); } function stopSurface(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 createPortal( children: ReactNodeList, containerTag: number, key: ?string = null, ): ReactPortal { return createPortalImpl(children, containerTag, null, key); } setBatchingImplementation(batchedUpdatesImpl, discreteUpdates); const roots = new Map(); export { // This is needed for implementation details of TouchableNativeFeedback // Remove this once TouchableNativeFeedback doesn't use cloneElement findHostInstance_DEPRECATED, findNodeHandle, dispatchCommand, sendAccessibilityEvent, render, // Deprecated - this function is being renamed to stopSurface, use that instead. // TODO (T47576999): Delete this once it's no longer called from native code. unmountComponentAtNode, stopSurface, createPortal, // This export is typically undefined in production builds. // See the "enableGetInspectorDataForInstanceInProduction" flag. getInspectorDataForInstance, // The public instance has a reference to the internal instance handle. // This method allows it to acess the most recent shadow node for // the instance (it's only accessible through it). getNodeFromInternalInstanceHandle, // Fabric native methods to traverse the host tree return the same internal // instance handles we use to dispatch events. This provides a way to access // the public instances we created from them (potentially created lazily). getPublicInstanceFromInternalInstanceHandle, // DEV-only: isChildPublicInstance, }; injectIntoDevTools({ // $FlowExpectedError[incompatible-call] The type of `Instance` in `getClosestInstanceFromNode` does not match in Fabric and the legacy renderer, so it fails to typecheck here. findFiberByHostInstance: getClosestInstanceFromNode, bundleType: __DEV__ ? 1 : 0, version: ReactVersion, rendererPackageName: 'react-native-renderer', rendererConfig: { getInspectorDataForInstance, getInspectorDataForViewTag: getInspectorDataForViewTag, getInspectorDataForViewAtPoint: getInspectorDataForViewAtPoint.bind( null, findNodeHandle, ), }, });