first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,210 @@
/**
* 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 strict-local
* @format
*/
import type {
ReactDevToolsAgent,
ReactDevToolsGlobalHook,
} from '../Types/ReactDevToolsTypes';
import type {Props} from './AppContainer';
import ReactNativeStyleAttributes from '../Components/View/ReactNativeStyleAttributes';
import View from '../Components/View/View';
import DebuggingOverlay from '../Debugging/DebuggingOverlay';
import useSubscribeToDebuggingOverlayRegistry from '../Debugging/useSubscribeToDebuggingOverlayRegistry';
import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter';
import LogBoxNotificationContainer from '../LogBox/LogBoxNotificationContainer';
import StyleSheet from '../StyleSheet/StyleSheet';
import {RootTagContext, createRootTag} from './RootTag';
import * as React from 'react';
import {useRef} from 'react';
const {useEffect, useState, useCallback} = React;
const reactDevToolsHook: ReactDevToolsGlobalHook = (window: $FlowFixMe)
.__REACT_DEVTOOLS_GLOBAL_HOOK__;
// Required for React DevTools to view / edit React Native styles in Flipper.
// Flipper doesn't inject these values when initializing DevTools.
if (reactDevToolsHook) {
reactDevToolsHook.resolveRNStyle =
require('../StyleSheet/flattenStyle').default;
reactDevToolsHook.nativeStyleEditorValidAttributes = Object.keys(
ReactNativeStyleAttributes,
);
}
type InspectorDeferredProps = {
inspectedViewRef: InspectedViewRef,
onInspectedViewRerenderRequest: () => void,
reactDevToolsAgent?: ReactDevToolsAgent,
};
const InspectorDeferred = ({
inspectedViewRef,
onInspectedViewRerenderRequest,
reactDevToolsAgent,
}: InspectorDeferredProps) => {
// D39382967 adds a require cycle: InitializeCore -> AppContainer -> Inspector -> InspectorPanel -> ScrollView -> InitializeCore
// We can't remove it yet, fallback to dynamic require for now. This is the only reason why this logic is in a separate function.
const Inspector =
require('../../src/private/devsupport/devmenu/elementinspector/Inspector').default;
return (
<Inspector
inspectedViewRef={inspectedViewRef}
onRequestRerenderApp={onInspectedViewRerenderRequest}
reactDevToolsAgent={reactDevToolsAgent}
/>
);
};
type ReactDevToolsOverlayDeferredProps = {
inspectedViewRef: InspectedViewRef,
reactDevToolsAgent: ReactDevToolsAgent,
};
const ReactDevToolsOverlayDeferred = ({
inspectedViewRef,
reactDevToolsAgent,
}: ReactDevToolsOverlayDeferredProps) => {
const ReactDevToolsOverlay =
require('../../src/private/devsupport/devmenu/elementinspector/ReactDevToolsOverlay').default;
return (
<ReactDevToolsOverlay
inspectedViewRef={inspectedViewRef}
reactDevToolsAgent={reactDevToolsAgent}
/>
);
};
const AppContainer = ({
children,
fabric,
initialProps,
internal_excludeInspector = false,
internal_excludeLogBox = false,
rootTag,
WrapperComponent,
rootViewStyle,
}: Props): React.Node => {
const appContainerRootViewRef: AppContainerRootViewRef = useRef(null);
const innerViewRef: InspectedViewRef = useRef(null);
const debuggingOverlayRef: DebuggingOverlayRef = useRef(null);
useSubscribeToDebuggingOverlayRegistry(
appContainerRootViewRef,
debuggingOverlayRef,
);
const [key, setKey] = useState(0);
const [shouldRenderInspector, setShouldRenderInspector] = useState(false);
const [reactDevToolsAgent, setReactDevToolsAgent] =
useState<ReactDevToolsAgent | void>(reactDevToolsHook?.reactDevtoolsAgent);
useEffect(() => {
let inspectorSubscription = null;
if (!internal_excludeInspector) {
inspectorSubscription = RCTDeviceEventEmitter.addListener(
'toggleElementInspector',
() => setShouldRenderInspector(value => !value),
);
}
let reactDevToolsAgentListener = null;
// If this is first render, subscribe to the event from React DevTools hook
if (reactDevToolsHook != null && reactDevToolsAgent == null) {
reactDevToolsAgentListener = setReactDevToolsAgent;
reactDevToolsHook.on?.('react-devtools', reactDevToolsAgentListener);
}
return () => {
inspectorSubscription?.remove();
if (
reactDevToolsHook?.off != null &&
reactDevToolsAgentListener != null
) {
reactDevToolsHook.off('react-devtools', reactDevToolsAgentListener);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
let innerView: React.Node = (
<View
collapsable={reactDevToolsAgent == null && !shouldRenderInspector}
pointerEvents="box-none"
key={key}
style={rootViewStyle || styles.container}
ref={innerViewRef}>
{children}
</View>
);
if (WrapperComponent != null) {
innerView = (
<WrapperComponent initialProps={initialProps} fabric={fabric === true}>
{innerView}
</WrapperComponent>
);
}
const onInspectedViewRerenderRequest = useCallback(
() => setKey(k => k + 1),
[],
);
return (
<RootTagContext.Provider value={createRootTag(rootTag)}>
<View
ref={appContainerRootViewRef}
style={rootViewStyle || styles.container}
pointerEvents="box-none">
{innerView}
<DebuggingOverlay ref={debuggingOverlayRef} />
{reactDevToolsAgent != null && (
<ReactDevToolsOverlayDeferred
inspectedViewRef={innerViewRef}
reactDevToolsAgent={reactDevToolsAgent}
/>
)}
{shouldRenderInspector && (
<InspectorDeferred
inspectedViewRef={innerViewRef}
onInspectedViewRerenderRequest={onInspectedViewRerenderRequest}
reactDevToolsAgent={reactDevToolsAgent}
/>
)}
{!internal_excludeLogBox && <LogBoxNotificationContainer />}
</View>
</RootTagContext.Provider>
);
};
const styles = StyleSheet.create({
container: {flex: 1},
});
export type AppContainerRootViewRef = React.RefObject<React.ElementRef<
typeof View,
> | null>;
export type InspectedViewRef = React.RefObject<React.ElementRef<
typeof View,
> | null>;
export type DebuggingOverlayRef = React.RefObject<React.ElementRef<
typeof DebuggingOverlay,
> | null>;
export default AppContainer;

View File

@@ -0,0 +1,49 @@
/**
* 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 strict-local
* @format
*/
import type {Props} from './AppContainer';
import View from '../Components/View/View';
import StyleSheet from '../StyleSheet/StyleSheet';
import {RootTagContext, createRootTag} from './RootTag';
import * as React from 'react';
const AppContainer = ({
children,
fabric,
initialProps,
rootTag,
WrapperComponent,
rootViewStyle,
}: Props): React.Node => {
let innerView = children;
if (WrapperComponent != null) {
innerView = (
<WrapperComponent initialProps={initialProps} fabric={fabric === true}>
{innerView}
</WrapperComponent>
);
}
return (
<RootTagContext.Provider value={createRootTag(rootTag)}>
<View style={rootViewStyle || styles.root} pointerEvents="box-none">
{innerView}
</View>
</RootTagContext.Provider>
);
};
const styles = StyleSheet.create({
root: {flex: 1},
});
export default AppContainer;

View File

@@ -0,0 +1,31 @@
/**
* 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
* @format
*/
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {RootTag} from './RootTag';
import * as React from 'react';
export type Props = $ReadOnly<{
children?: React.Node,
fabric?: boolean,
rootTag: number | RootTag,
initialProps?: {...},
WrapperComponent?: ?React.ComponentType<any>,
rootViewStyle?: ?ViewStyleProp,
internal_excludeLogBox?: boolean,
internal_excludeInspector?: boolean,
}>;
const AppContainer: component(...Props) = __DEV__
? require('./AppContainer-dev').default
: require('./AppContainer-prod').default;
export default AppContainer;

View File

@@ -0,0 +1,121 @@
/**
* 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.
*
* @format
*/
import type * as React from 'react';
import type {IPerformanceLogger} from '../Utilities/IPerformanceLogger';
import type {ViewStyle} from '../StyleSheet/StyleSheetTypes';
type Task = (taskData: any) => Promise<void>;
type TaskProvider = () => Task;
type TaskCanceller = () => void;
type TaskCancelProvider = () => TaskCanceller;
export type ComponentProvider = () => React.ComponentType<any>;
export type Runnable = (appParameters: any) => void;
export type AppConfig = {
appKey: string;
component?: ComponentProvider | undefined;
run?: Runnable | undefined;
};
export type ComponentProviderInstrumentationHook = (
component: ComponentProvider,
scopedPerformanceLogger: IPerformanceLogger,
) => React.ComponentType<any>;
export type WrapperComponentProvider = (
appParameters: any,
) => React.ComponentType<any>;
export type RootViewStyleProvider = (appParameters: any) => ViewStyle;
/**
* `AppRegistry` is the JS entry point to running all React Native apps. App
* root components should register themselves with
* `AppRegistry.registerComponent`, then the native system can load the bundle
* for the app and then actually run the app when it's ready by invoking
* `AppRegistry.runApplication`.
*
* To "stop" an application when a view should be destroyed, call
* `AppRegistry.unmountApplicationComponentAtRootTag` with the tag that was
* pass into `runApplication`. These should always be used as a pair.
*
* `AppRegistry` should be `require`d early in the `require` sequence to make
* sure the JS execution environment is setup before other modules are
* `require`d.
*/
export namespace AppRegistry {
export function setWrapperComponentProvider(
provider: WrapperComponentProvider,
): void;
export function setRootViewStyleProvider(
provider: RootViewStyleProvider,
): void;
export function registerConfig(config: AppConfig[]): void;
export function registerComponent(
appKey: string,
getComponentFunc: ComponentProvider,
section?: boolean,
): string;
export function registerRunnable(appKey: string, func: Runnable): string;
export function registerSection(
appKey: string,
component: ComponentProvider,
): void;
export function getAppKeys(): string[];
export function getSectionKeys(): string[];
export function getSections(): Record<string, Runnable>;
export function unmountApplicationComponentAtRootTag(rootTag: number): void;
export function runApplication(appKey: string, appParameters: any): void;
export function setSurfaceProps(
appKey: string,
appParameters: any,
displayMode?: number,
): void;
export function getRunnable(appKey: string): Runnable | undefined;
export function getRegistry(): {sections: string[]; runnables: Runnable[]};
export function setComponentProviderInstrumentationHook(
hook: ComponentProviderInstrumentationHook,
): void;
export function registerHeadlessTask(
taskKey: string,
taskProvider: TaskProvider,
): void;
export function registerCancellableHeadlessTask(
taskKey: string,
taskProvider: TaskProvider,
taskCancelProvider: TaskCancelProvider,
): void;
export function startHeadlessTask(
taskId: number,
taskKey: string,
data: any,
): void;
export function cancelHeadlessTask(taskId: number, taskKey: string): void;
}

View File

@@ -0,0 +1,49 @@
/**
* 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
* @format
*/
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {RootTag} from '../Types/RootTagTypes';
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
import type {DisplayModeType} from './DisplayMode';
type HeadlessTask = (taskData: any) => Promise<void>;
export type TaskProvider = () => HeadlessTask;
export type ComponentProvider = () => React.ComponentType<any>;
export type ComponentProviderInstrumentationHook = (
component_: ComponentProvider,
scopedPerformanceLogger: IPerformanceLogger,
) => React.ComponentType<any>;
export type AppConfig = {
appKey: string,
component?: ComponentProvider,
run?: Runnable,
section?: boolean,
...
};
export type AppParameters = {
initialProps: $ReadOnly<{[string]: mixed, ...}>,
rootTag: RootTag,
fabric?: boolean,
};
export type Runnable = (
appParameters: AppParameters,
displayMode: DisplayModeType,
) => void;
export type Runnables = {[appKey: string]: Runnable};
export type Registry = {
sections: $ReadOnlyArray<string>,
runnables: Runnables,
...
};
export type WrapperComponentProvider = (
appParameters: Object,
) => React.ComponentType<any>;
export type RootViewStyleProvider = (appParameters: Object) => ViewStyleProp;

View File

@@ -0,0 +1,40 @@
/**
* 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
* @format
*/
import registerCallableModule from '../Core/registerCallableModule';
/**
* `AppRegistry` is the JavaScript entry point to running all React Native apps.
*
* See https://reactnative.dev/docs/appregistry
*/
import * as AppRegistry from './AppRegistryImpl';
// Register LogBox as a default surface
AppRegistry.registerComponent('LogBox', () => {
if (__DEV__ && typeof jest === 'undefined') {
return require('../LogBox/LogBoxInspectorContainer').default;
} else {
return function NoOp() {
return null;
};
}
});
global.RN$AppRegistry = AppRegistry;
// Backwards compat with SurfaceRegistry, remove me later
global.RN$SurfaceRegistry = {
renderSurface: AppRegistry.runApplication,
setSurfaceProps: AppRegistry.setSurfaceProps,
};
registerCallableModule('AppRegistry', AppRegistry);
export {AppRegistry};

View File

@@ -0,0 +1,23 @@
/**
* 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
* @format
*/
export type {
TaskProvider,
ComponentProvider,
ComponentProviderInstrumentationHook,
AppConfig,
Runnable,
Runnables,
Registry,
WrapperComponentProvider,
RootViewStyleProvider,
} from './AppRegistry.flow';
export * as AppRegistry from './AppRegistryImpl';

View File

@@ -0,0 +1,307 @@
/**
* 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
* @format
*/
import type {RootTag} from '../Types/RootTagTypes';
import type {
AppConfig,
AppParameters,
ComponentProvider,
ComponentProviderInstrumentationHook,
Registry,
RootViewStyleProvider,
Runnable,
Runnables,
TaskProvider,
WrapperComponentProvider,
} from './AppRegistry.flow';
import createPerformanceLogger from '../Utilities/createPerformanceLogger';
import SceneTracker from '../Utilities/SceneTracker';
import {coerceDisplayMode} from './DisplayMode';
import HeadlessJsTaskError from './HeadlessJsTaskError';
import {unmountComponentAtNodeAndRemoveContainer} from './RendererProxy';
import invariant from 'invariant';
type TaskCanceller = () => void;
type TaskCancelProvider = () => TaskCanceller;
const runnables: Runnables = {};
const sections: Runnables = {};
const taskProviders: Map<string, TaskProvider> = new Map();
const taskCancelProviders: Map<string, TaskCancelProvider> = new Map();
let componentProviderInstrumentationHook: ComponentProviderInstrumentationHook =
(component: ComponentProvider) => component();
let wrapperComponentProvider: ?WrapperComponentProvider;
let rootViewStyleProvider: ?RootViewStyleProvider;
export function setWrapperComponentProvider(
provider: WrapperComponentProvider,
) {
wrapperComponentProvider = provider;
}
export function setRootViewStyleProvider(provider: RootViewStyleProvider) {
rootViewStyleProvider = provider;
}
export function registerConfig(config: Array<AppConfig>): void {
config.forEach(appConfig => {
if (appConfig.run) {
registerRunnable(appConfig.appKey, appConfig.run);
} else {
invariant(
appConfig.component != null,
'AppRegistry.registerConfig(...): Every config is expected to set ' +
'either `run` or `component`, but `%s` has neither.',
appConfig.appKey,
);
registerComponent(
appConfig.appKey,
appConfig.component,
appConfig.section,
);
}
});
}
/**
* Registers an app's root component.
*
* See https://reactnative.dev/docs/appregistry#registercomponent
*/
export function registerComponent(
appKey: string,
componentProvider: ComponentProvider,
section?: boolean,
): string {
const scopedPerformanceLogger = createPerformanceLogger();
runnables[appKey] = (appParameters, displayMode) => {
const renderApplication = require('./renderApplication').default;
renderApplication(
componentProviderInstrumentationHook(
componentProvider,
scopedPerformanceLogger,
),
appParameters.initialProps,
appParameters.rootTag,
wrapperComponentProvider && wrapperComponentProvider(appParameters),
rootViewStyleProvider && rootViewStyleProvider(appParameters),
appParameters.fabric,
scopedPerformanceLogger,
appKey === 'LogBox', // is logbox
appKey,
displayMode,
);
};
if (section) {
sections[appKey] = runnables[appKey];
}
return appKey;
}
export function registerRunnable(appKey: string, run: Runnable): string {
runnables[appKey] = run;
return appKey;
}
export function registerSection(
appKey: string,
component: ComponentProvider,
): void {
registerComponent(appKey, component, true);
}
export function getAppKeys(): $ReadOnlyArray<string> {
return Object.keys(runnables);
}
export function getSectionKeys(): $ReadOnlyArray<string> {
return Object.keys(sections);
}
export function getSections(): Runnables {
return {
...sections,
};
}
export function getRunnable(appKey: string): ?Runnable {
return runnables[appKey];
}
export function getRegistry(): Registry {
return {
sections: getSectionKeys(),
runnables: {...runnables},
};
}
export function setComponentProviderInstrumentationHook(
hook: ComponentProviderInstrumentationHook,
) {
componentProviderInstrumentationHook = hook;
}
/**
* Loads the JavaScript bundle and runs the app.
*
* See https://reactnative.dev/docs/appregistry#runapplication
*/
export function runApplication(
appKey: string,
appParameters: AppParameters,
displayMode?: number,
): void {
if (appKey !== 'LogBox') {
const logParams = __DEV__ ? ` with ${JSON.stringify(appParameters)}` : '';
const msg = `Running "${appKey}"${logParams}`;
console.log(msg);
}
invariant(
runnables[appKey],
`"${appKey}" has not been registered. This can happen if:\n` +
'* Metro (the local dev server) is run from the wrong folder. ' +
'Check if Metro is running, stop it and restart it in the current project.\n' +
"* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.",
);
SceneTracker.setActiveScene({name: appKey});
runnables[appKey](appParameters, coerceDisplayMode(displayMode));
}
/**
* Update initial props for a surface that's already rendered
*/
export function setSurfaceProps(
appKey: string,
appParameters: Object,
displayMode?: number,
): void {
if (appKey !== 'LogBox') {
const msg =
'Updating props for Surface "' +
appKey +
'" with ' +
JSON.stringify(appParameters);
console.log(msg);
}
invariant(
runnables[appKey],
`"${appKey}" has not been registered. This can happen if:\n` +
'* Metro (the local dev server) is run from the wrong folder. ' +
'Check if Metro is running, stop it and restart it in the current project.\n' +
"* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.",
);
runnables[appKey](appParameters, coerceDisplayMode(displayMode));
}
/**
* Stops an application when a view should be destroyed.
*
* See https://reactnative.dev/docs/appregistry#unmountapplicationcomponentatroottag
*/
export function unmountApplicationComponentAtRootTag(rootTag: RootTag): void {
unmountComponentAtNodeAndRemoveContainer(rootTag);
}
/**
* Register a headless task. A headless task is a bit of code that runs without a UI.
*
* See https://reactnative.dev/docs/appregistry#registerheadlesstask
*/
export function registerHeadlessTask(
taskKey: string,
taskProvider: TaskProvider,
): void {
// $FlowFixMe[object-this-reference]
registerCancellableHeadlessTask(taskKey, taskProvider, () => () => {
/* Cancel is no-op */
});
}
/**
* Register a cancellable headless task. A headless task is a bit of code that runs without a UI.
*
* See https://reactnative.dev/docs/appregistry#registercancellableheadlesstask
*/
export function registerCancellableHeadlessTask(
taskKey: string,
taskProvider: TaskProvider,
taskCancelProvider: TaskCancelProvider,
): void {
if (taskProviders.has(taskKey)) {
console.warn(
`registerHeadlessTask or registerCancellableHeadlessTask called multiple times for same key '${taskKey}'`,
);
}
taskProviders.set(taskKey, taskProvider);
taskCancelProviders.set(taskKey, taskCancelProvider);
}
/**
* Only called from native code. Starts a headless task.
*
* See https://reactnative.dev/docs/appregistry#startheadlesstask
*/
export function startHeadlessTask(
taskId: number,
taskKey: string,
data: any,
): void {
const NativeHeadlessJsTaskSupport =
require('./NativeHeadlessJsTaskSupport').default;
const taskProvider = taskProviders.get(taskKey);
if (!taskProvider) {
console.warn(`No task registered for key ${taskKey}`);
if (NativeHeadlessJsTaskSupport) {
NativeHeadlessJsTaskSupport.notifyTaskFinished(taskId);
}
return;
}
taskProvider()(data)
.then(() => {
if (NativeHeadlessJsTaskSupport) {
NativeHeadlessJsTaskSupport.notifyTaskFinished(taskId);
}
})
.catch(reason => {
console.error(reason);
if (
NativeHeadlessJsTaskSupport &&
reason instanceof HeadlessJsTaskError
) {
// $FlowFixMe[unused-promise]
NativeHeadlessJsTaskSupport.notifyTaskRetry(taskId).then(
retryPosted => {
if (!retryPosted) {
NativeHeadlessJsTaskSupport.notifyTaskFinished(taskId);
}
},
);
}
});
}
/**
* Only called from native code. Cancels a headless task.
*
* See https://reactnative.dev/docs/appregistry#cancelheadlesstask
*/
export function cancelHeadlessTask(taskId: number, taskKey: string): void {
const taskCancelProvider = taskCancelProviders.get(taskKey);
if (!taskCancelProvider) {
throw new Error(`No task canceller registered for key '${taskKey}'`);
}
taskCancelProvider()();
}

View File

@@ -0,0 +1,414 @@
/**
* 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
* @format
*/
'use strict';
import type {RootTag} from '../Types/RootTagTypes';
import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface';
import {unstable_hasComponent} from '../NativeComponent/NativeComponentRegistryUnstable';
import defineLazyObjectProperty from '../Utilities/defineLazyObjectProperty';
import Platform from '../Utilities/Platform';
import {getFabricUIManager} from './FabricUIManager';
import nullthrows from 'nullthrows';
function raiseSoftError(methodName: string, details?: string): void {
console.error(
`[ReactNative Architecture][JS] '${methodName}' is not available in the new React Native architecture.` +
(details ? ` ${details}` : ''),
);
}
const getUIManagerConstants: ?() => {[viewManagerName: string]: Object} =
global.RN$LegacyInterop_UIManager_getConstants;
const getUIManagerConstantsCached = (function () {
let wasCalledOnce = false;
let result: {[viewManagerName: string]: Object} = {};
return (): {[viewManagerName: string]: Object} => {
if (!wasCalledOnce) {
result = nullthrows(getUIManagerConstants)();
wasCalledOnce = true;
}
return result;
};
})();
const getConstantsForViewManager: ?(viewManagerName: string) => ?Object =
global.RN$LegacyInterop_UIManager_getConstantsForViewManager;
const getDefaultEventTypes: ?() => Object =
global.RN$LegacyInterop_UIManager_getDefaultEventTypes;
const getDefaultEventTypesCached = (function () {
let wasCalledOnce = false;
let result = null;
return (): Object => {
if (!wasCalledOnce) {
result = nullthrows(getDefaultEventTypes)();
wasCalledOnce = true;
}
return result;
};
})();
/**
* UIManager.js overrides these APIs.
* Pull them out from the BridgelessUIManager implementation. So, we can ignore them.
*/
const UIManagerJSOverridenAPIs = {
measure: (
reactTag: number,
callback: (
left: number,
top: number,
width: number,
height: number,
pageX: number,
pageY: number,
) => void,
): void => {
raiseSoftError('measure');
},
measureInWindow: (
reactTag: number,
callback: (x: number, y: number, width: number, height: number) => void,
): void => {
raiseSoftError('measureInWindow');
},
measureLayout: (
reactTag: number,
ancestorReactTag: number,
errorCallback: (error: Object) => void,
callback: (
left: number,
top: number,
width: number,
height: number,
) => void,
): void => {
raiseSoftError('measureLayout');
},
measureLayoutRelativeToParent: (
reactTag: number,
errorCallback: (error: Object) => void,
callback: (
left: number,
top: number,
width: number,
height: number,
) => void,
): void => {
raiseSoftError('measureLayoutRelativeToParent');
},
dispatchViewManagerCommand: (
reactTag: number,
commandID: number,
commandArgs: ?Array<string | number | boolean>,
): void => {
raiseSoftError('dispatchViewManagerCommand');
},
};
/**
* Leave Unimplemented: The only thing that called these methods was the paper renderer.
* In OSS, the New Architecture will just use the Fabric renderer, which uses
* different APIs.
*/
const UIManagerJSUnusedInNewArchAPIs = {
createView: (
reactTag: number,
viewName: string,
rootTag: RootTag,
props: Object,
): void => {
raiseSoftError('createView');
},
updateView: (reactTag: number, viewName: string, props: Object): void => {
raiseSoftError('updateView');
},
setChildren: (containerTag: number, reactTags: Array<number>): void => {
raiseSoftError('setChildren');
},
manageChildren: (
containerTag: number,
moveFromIndices: Array<number>,
moveToIndices: Array<number>,
addChildReactTags: Array<number>,
addAtIndices: Array<number>,
removeAtIndices: Array<number>,
): void => {
raiseSoftError('manageChildren');
},
setJSResponder: (reactTag: number, blockNativeResponder: boolean): void => {
raiseSoftError('setJSResponder');
},
clearJSResponder: (): void => {
raiseSoftError('clearJSResponder');
},
};
/**
* Leave unimplemented: These APIs are deprecated in UIManager. We will eventually remove
* them from React Native.
*/
const UIManagerJSDeprecatedPlatformAPIs = Platform.select({
android: {},
});
const UIManagerJSPlatformAPIs = Platform.select({
android: {
getConstantsForViewManager: (viewManagerName: string): ?Object => {
if (getConstantsForViewManager) {
return getConstantsForViewManager(viewManagerName);
}
raiseSoftError('getConstantsForViewManager');
return {};
},
getDefaultEventTypes: (): Array<string> => {
if (getDefaultEventTypes) {
return getDefaultEventTypesCached();
}
raiseSoftError('getDefaultEventTypes');
return [];
},
setLayoutAnimationEnabledExperimental: (enabled: boolean): void => {
if (__DEV__) {
console.warn(
'setLayoutAnimationEnabledExperimental is currently a no-op in the New Architecture.',
);
}
},
sendAccessibilityEvent: (reactTag: number, eventType: number): void => {
// Keep this in sync with java:FabricUIManager.sendAccessibilityEventFromJS
// and legacySendAccessibilityEvent.android.js
const AccessibilityEvent = {
TYPE_VIEW_FOCUSED: 0x00000008,
TYPE_WINDOW_STATE_CHANGED: 0x00000020,
TYPE_VIEW_CLICKED: 0x00000001,
TYPE_VIEW_HOVER_ENTER: 0x00000080,
};
let eventName = null;
if (eventType === AccessibilityEvent.TYPE_VIEW_FOCUSED) {
eventName = 'focus';
} else if (eventType === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
eventName = 'windowStateChange';
} else if (eventType === AccessibilityEvent.TYPE_VIEW_CLICKED) {
eventName = 'click';
} else if (eventType === AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) {
eventName = 'viewHoverEnter';
} else {
console.error(
`sendAccessibilityEvent() dropping event: Called with unsupported eventType: ${eventType}`,
);
return;
}
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (!shadowNode) {
console.error(
`sendAccessibilityEvent() dropping event: Cannot find view with tag #${reactTag}`,
);
return;
}
FabricUIManager.sendAccessibilityEvent(shadowNode, eventName);
},
},
ios: {
/**
* TODO(T174674274): Implement lazy loading of legacy view managers in the new architecture.
*
* Leave this unimplemented until we implement lazy loading of legacy modules and view managers in the new architecture.
*/
lazilyLoadView: (name: string): Object => {
raiseSoftError('lazilyLoadView');
return {};
},
focus: (reactTag: number): void => {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (!shadowNode) {
console.error(`focus() noop: Cannot find view with tag #${reactTag}`);
return;
}
FabricUIManager.dispatchCommand(shadowNode, 'focus', []);
},
blur: (reactTag: number): void => {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (!shadowNode) {
console.error(`blur() noop: Cannot find view with tag #${reactTag}`);
return;
}
FabricUIManager.dispatchCommand(shadowNode, 'blur', []);
},
},
});
const UIManagerJS: UIManagerJSInterface & {[string]: any} = {
...UIManagerJSOverridenAPIs,
...UIManagerJSDeprecatedPlatformAPIs,
...UIManagerJSPlatformAPIs,
...UIManagerJSUnusedInNewArchAPIs,
getViewManagerConfig: (viewManagerName: string): mixed => {
if (getUIManagerConstants) {
const constants = getUIManagerConstantsCached();
if (
!constants[viewManagerName] &&
UIManagerJS.getConstantsForViewManager
) {
constants[viewManagerName] =
UIManagerJS.getConstantsForViewManager(viewManagerName);
}
return constants[viewManagerName];
} else {
raiseSoftError(
`getViewManagerConfig('${viewManagerName}')`,
`If '${viewManagerName}' has a ViewManager and you want to retrieve its native ViewConfig, please turn on the native ViewConfig interop layer. If you want to see if this component is registered with React Native, please call hasViewManagerConfig('${viewManagerName}') instead.`,
);
return null;
}
},
hasViewManagerConfig: (viewManagerName: string): boolean => {
return unstable_hasComponent(viewManagerName);
},
getConstants: (): Object => {
if (getUIManagerConstants) {
return getUIManagerConstantsCached();
} else {
raiseSoftError('getConstants');
return null;
}
},
findSubviewIn: (
reactTag: number,
point: Array<number>,
callback: (
nativeViewTag: number,
left: number,
top: number,
width: number,
height: number,
) => void,
): void => {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (!shadowNode) {
console.error(
`findSubviewIn() noop: Cannot find view with reactTag ${reactTag}`,
);
return;
}
FabricUIManager.findNodeAtPoint(
shadowNode,
point[0],
point[1],
function (internalInstanceHandle) {
if (internalInstanceHandle == null) {
console.error('findSubviewIn(): Cannot find node at point');
return;
}
let instanceHandle: Object = internalInstanceHandle;
let node = instanceHandle.stateNode.node;
if (!node) {
console.error('findSubviewIn(): Cannot find node at point');
return;
}
let nativeViewTag: number =
instanceHandle.stateNode.canonical.nativeTag;
FabricUIManager.measure(
node,
function (x, y, width, height, pageX, pageY) {
callback(nativeViewTag, pageX, pageY, width, height);
},
);
},
);
},
viewIsDescendantOf: (
reactTag: number,
ancestorReactTag: number,
callback: (result: Array<boolean>) => void,
): void => {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode = FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (!shadowNode) {
console.error(
`viewIsDescendantOf() noop: Cannot find view with reactTag ${reactTag}`,
);
return;
}
const ancestorShadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(ancestorReactTag);
if (!ancestorShadowNode) {
console.error(
`viewIsDescendantOf() noop: Cannot find view with ancestorReactTag ${ancestorReactTag}`,
);
return;
}
// Keep this in sync with ReadOnlyNode.js
const DOCUMENT_POSITION_CONTAINED_BY = 16;
let result = FabricUIManager.compareDocumentPosition(
ancestorShadowNode,
shadowNode,
);
// eslint-disable-next-line no-bitwise
let isAncestor = (result & DOCUMENT_POSITION_CONTAINED_BY) !== 0;
callback([isAncestor]);
},
configureNextLayoutAnimation: (
config: Object,
callback: () => void,
errorCallback: (error: Object) => void,
): void => {
const FabricUIManager = nullthrows(getFabricUIManager());
FabricUIManager.configureNextLayoutAnimation(
config,
callback,
errorCallback,
);
},
};
if (getUIManagerConstants) {
Object.keys(getUIManagerConstantsCached()).forEach(viewConfigName => {
UIManagerJS[viewConfigName] = getUIManagerConstantsCached()[viewConfigName];
});
if (UIManagerJS.getConstants().ViewManagerNames) {
UIManagerJS.getConstants().ViewManagerNames.forEach(viewManagerName => {
defineLazyObjectProperty(UIManagerJS, viewManagerName, {
get: () =>
nullthrows(UIManagerJS.getConstantsForViewManager)(viewManagerName),
});
});
}
}
export default UIManagerJS;

View File

@@ -0,0 +1,32 @@
/**
* 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 strict-local
* @format
*/
export opaque type DisplayModeType = number;
/** DisplayMode should be in sync with the method displayModeToInt from
* react/renderer/uimanager/primitives.h. */
const DisplayMode: {+[string]: DisplayModeType} = Object.freeze({
VISIBLE: 1,
SUSPENDED: 2,
HIDDEN: 3,
});
export function coerceDisplayMode(value: ?number): DisplayModeType {
switch (value) {
case DisplayMode.SUSPENDED:
return DisplayMode.SUSPENDED;
case DisplayMode.HIDDEN:
return DisplayMode.HIDDEN;
default:
return DisplayMode.VISIBLE;
}
}
export default DisplayMode;

View File

@@ -0,0 +1,167 @@
/**
* 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 strict-local
* @format
*/
'use strict';
import type {
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
} from '../../src/private/types/HostInstance';
import type {NativeElementReference} from '../../src/private/webapis/dom/nodes/specs/NativeDOM';
import type {
InternalInstanceHandle,
LayoutAnimationConfig,
Node,
} from '../Renderer/shims/ReactNativeTypes';
import type {RootTag} from '../Types/RootTagTypes';
import defineLazyObjectProperty from '../Utilities/defineLazyObjectProperty';
export type NodeSet = Array<Node>;
export type NodeProps = {...};
export interface Spec {
+createNode: (
reactTag: number,
viewName: string,
rootTag: RootTag,
props: NodeProps,
instanceHandle: InternalInstanceHandle,
) => Node;
+cloneNode: (node: Node) => Node;
+cloneNodeWithNewChildren: (node: Node) => Node;
+cloneNodeWithNewProps: (node: Node, newProps: NodeProps) => Node;
+cloneNodeWithNewChildrenAndProps: (node: Node, newProps: NodeProps) => Node;
+createChildSet: (rootTag: RootTag) => NodeSet;
+appendChild: (parentNode: Node, child: Node) => Node;
+appendChildToSet: (childSet: NodeSet, child: Node) => void;
+completeRoot: (rootTag: RootTag, childSet: NodeSet) => void;
+measure: (
node: Node | NativeElementReference,
callback: MeasureOnSuccessCallback,
) => void;
+measureInWindow: (
node: Node | NativeElementReference,
callback: MeasureInWindowOnSuccessCallback,
) => void;
+measureLayout: (
node: Node | NativeElementReference,
relativeNode: Node | NativeElementReference,
onFail: () => void,
onSuccess: MeasureLayoutOnSuccessCallback,
) => void;
+configureNextLayoutAnimation: (
config: LayoutAnimationConfig,
callback: () => void, // check what is returned here
errorCallback: () => void,
) => void;
+sendAccessibilityEvent: (node: Node, eventType: string) => void;
+findShadowNodeByTag_DEPRECATED: (reactTag: number) => ?Node;
+setNativeProps: (
node: Node | NativeElementReference,
newProps: NodeProps,
) => void;
+dispatchCommand: (
node: Node,
commandName: string,
args: Array<mixed>,
) => void;
+findNodeAtPoint: (
node: Node,
locationX: number,
locationY: number,
callback: (instanceHandle: ?InternalInstanceHandle) => void,
) => void;
+compareDocumentPosition: (
node: Node | NativeElementReference,
otherNode: Node | NativeElementReference,
) => number;
+getBoundingClientRect: (
node: Node | NativeElementReference,
includeTransform: boolean,
) => ?[
/* x: */ number,
/* y: */ number,
/* width: */ number,
/* height: */ number,
];
+unstable_DefaultEventPriority: number;
+unstable_DiscreteEventPriority: number;
+unstable_ContinuousEventPriority: number;
+unstable_IdleEventPriority: number;
+unstable_getCurrentEventPriority: () => number;
}
let nativeFabricUIManagerProxy: ?Spec;
// This is a list of all the methods in global.nativeFabricUIManager that we'll
// cache in JavaScript, as the current implementation of the binding
// creates a new host function every time methods are accessed.
const CACHED_PROPERTIES = [
'createNode',
'cloneNode',
'cloneNodeWithNewChildren',
'cloneNodeWithNewProps',
'cloneNodeWithNewChildrenAndProps',
'createChildSet',
'appendChild',
'appendChildToSet',
'completeRoot',
'measure',
'measureInWindow',
'measureLayout',
'configureNextLayoutAnimation',
'sendAccessibilityEvent',
'findShadowNodeByTag_DEPRECATED',
'setNativeProps',
'dispatchCommand',
'compareDocumentPosition',
'getBoundingClientRect',
'unstable_DefaultEventPriority',
'unstable_DiscreteEventPriority',
'unstable_ContinuousEventPriority',
'unstable_IdleEventPriority',
'unstable_getCurrentEventPriority',
];
// This is exposed as a getter because apps using the legacy renderer AND
// Fabric can define the binding lazily. If we evaluated the global and cached
// it in the module we might be caching an `undefined` value before it is set.
export function getFabricUIManager(): ?Spec {
if (
nativeFabricUIManagerProxy == null &&
global.nativeFabricUIManager != null
) {
nativeFabricUIManagerProxy = createProxyWithCachedProperties(
global.nativeFabricUIManager,
CACHED_PROPERTIES,
);
}
return nativeFabricUIManagerProxy;
}
/**
*
* Returns an object that caches the specified properties the first time they
* are accessed, and falls back to the original object for other properties.
*/
function createProxyWithCachedProperties(
implementation: Spec,
propertiesToCache: $ReadOnlyArray<string>,
): Spec {
const proxy = Object.create(implementation);
for (const propertyName of propertiesToCache) {
defineLazyObjectProperty(proxy, propertyName, {
// $FlowExpectedError[prop-missing]
get: () => implementation[propertyName],
});
}
return proxy;
}

View File

@@ -0,0 +1,11 @@
/**
* 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 strict-local
* @format
*/
export default class HeadlessJsTaskError extends Error {}

View File

@@ -0,0 +1,25 @@
/**
* 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.
*
* @format
*/
export interface I18nManagerStatic {
getConstants: () => {
isRTL: boolean;
doLeftAndRightSwapInRTL: boolean;
localeIdentifier?: string | null | undefined;
};
allowRTL: (allowRTL: boolean) => void;
forceRTL: (forceRTL: boolean) => void;
swapLeftAndRightInRTL: (swapLeftAndRight: boolean) => void;
isRTL: boolean;
doLeftAndRightSwapInRTL: boolean;
}
/** https://reactnative.dev/blog/2016/08/19/right-to-left-support-for-react-native-apps */
export const I18nManager: I18nManagerStatic;
export type I18nManager = I18nManagerStatic;

View File

@@ -0,0 +1,62 @@
/**
* 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 strict-local
* @format
*/
import type {I18nManagerConstants} from './NativeI18nManager';
import NativeI18nManager from './NativeI18nManager';
const i18nConstants: I18nManagerConstants = getI18nManagerConstants();
function getI18nManagerConstants(): I18nManagerConstants {
if (NativeI18nManager) {
const {isRTL, doLeftAndRightSwapInRTL, localeIdentifier} =
NativeI18nManager.getConstants();
return {isRTL, doLeftAndRightSwapInRTL, localeIdentifier};
}
return {
isRTL: false,
doLeftAndRightSwapInRTL: true,
};
}
export default {
getConstants: (): I18nManagerConstants => {
return i18nConstants;
},
allowRTL: (shouldAllow: boolean) => {
if (!NativeI18nManager) {
return;
}
NativeI18nManager.allowRTL(shouldAllow);
},
forceRTL: (shouldForce: boolean) => {
if (!NativeI18nManager) {
return;
}
NativeI18nManager.forceRTL(shouldForce);
},
swapLeftAndRightInRTL: (flipStyles: boolean) => {
if (!NativeI18nManager) {
return;
}
NativeI18nManager.swapLeftAndRightInRTL(flipStyles);
},
isRTL: i18nConstants.isRTL as I18nManagerConstants['isRTL'],
doLeftAndRightSwapInRTL:
i18nConstants.doLeftAndRightSwapInRTL as I18nManagerConstants['doLeftAndRightSwapInRTL'],
};

View File

@@ -0,0 +1,14 @@
/**
* 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 strict
* @format
*/
export * from '../../src/private/specs_DEPRECATED/modules/NativeHeadlessJsTaskSupport';
import NativeHeadlessJsTaskSupport from '../../src/private/specs_DEPRECATED/modules/NativeHeadlessJsTaskSupport';
export default NativeHeadlessJsTaskSupport;

View File

@@ -0,0 +1,14 @@
/**
* 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 strict
* @format
*/
export * from '../../src/private/specs_DEPRECATED/modules/NativeI18nManager';
import NativeI18nManager from '../../src/private/specs_DEPRECATED/modules/NativeI18nManager';
export default NativeI18nManager;

View File

@@ -0,0 +1,14 @@
/**
* 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 strict-local
* @format
*/
export * from '../../src/private/specs_DEPRECATED/modules/NativeUIManager';
import NativeUIManager from '../../src/private/specs_DEPRECATED/modules/NativeUIManager';
export default NativeUIManager;

View File

@@ -0,0 +1,191 @@
/**
* 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
* @format
*/
import type {RootTag} from '../Types/RootTagTypes';
import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface';
import NativeUIManager from './NativeUIManager';
import nullthrows from 'nullthrows';
const NativeModules = require('../BatchedBridge/NativeModules').default;
const defineLazyObjectProperty =
require('../Utilities/defineLazyObjectProperty').default;
const Platform = require('../Utilities/Platform').default;
const UIManagerProperties = require('./UIManagerProperties').default;
const viewManagerConfigs: {[string]: any | null} = {};
const triedLoadingConfig = new Set<string>();
let NativeUIManagerConstants = {};
let isNativeUIManagerConstantsSet = false;
function getConstants(): Object {
if (!isNativeUIManagerConstantsSet) {
NativeUIManagerConstants = NativeUIManager.getConstants();
isNativeUIManagerConstantsSet = true;
}
return NativeUIManagerConstants;
}
function getViewManagerConfig(viewManagerName: string): any {
if (
viewManagerConfigs[viewManagerName] === undefined &&
NativeUIManager.getConstantsForViewManager
) {
try {
viewManagerConfigs[viewManagerName] =
NativeUIManager.getConstantsForViewManager(viewManagerName);
} catch (e) {
console.error(
"NativeUIManager.getConstantsForViewManager('" +
viewManagerName +
"') threw an exception.",
e,
);
viewManagerConfigs[viewManagerName] = null;
}
}
const config = viewManagerConfigs[viewManagerName];
if (config) {
return config;
}
// If we're in the Chrome Debugger, let's not even try calling the sync
// method.
if (!global.nativeCallSyncHook) {
return config;
}
if (
NativeUIManager.lazilyLoadView &&
!triedLoadingConfig.has(viewManagerName)
) {
const result = nullthrows(NativeUIManager.lazilyLoadView)(viewManagerName);
triedLoadingConfig.add(viewManagerName);
if (result != null && result.viewConfig != null) {
getConstants()[viewManagerName] = result.viewConfig;
lazifyViewManagerConfig(viewManagerName);
}
}
return viewManagerConfigs[viewManagerName];
}
// $FlowFixMe[cannot-spread-interface]
const UIManagerJS: UIManagerJSInterface = {
...NativeUIManager,
createView(
reactTag: number,
viewName: string,
rootTag: RootTag,
props: Object,
): void {
if (Platform.OS === 'ios' && viewManagerConfigs[viewName] === undefined) {
// This is necessary to force the initialization of native viewManager
// classes in iOS when using static ViewConfigs
getViewManagerConfig(viewName);
}
NativeUIManager.createView(reactTag, viewName, rootTag, props);
},
getConstants(): Object {
return getConstants();
},
getViewManagerConfig(viewManagerName: string): any {
return getViewManagerConfig(viewManagerName);
},
hasViewManagerConfig(viewManagerName: string): boolean {
return getViewManagerConfig(viewManagerName) != null;
},
};
// TODO (T45220498): Remove this.
// 3rd party libs may be calling `NativeModules.UIManager.getViewManagerConfig()`
// instead of `UIManager.getViewManagerConfig()` off UIManager.js.
// This is a workaround for now.
// $FlowFixMe[prop-missing]
NativeUIManager.getViewManagerConfig = UIManagerJS.getViewManagerConfig;
function lazifyViewManagerConfig(viewName: string) {
const viewConfig = getConstants()[viewName];
viewManagerConfigs[viewName] = viewConfig;
if (viewConfig.Manager) {
defineLazyObjectProperty(viewConfig, 'Constants', {
get: () => {
const viewManager = NativeModules[viewConfig.Manager];
const constants: {[string]: mixed} = {};
viewManager &&
Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
if (typeof value !== 'function') {
constants[key] = value;
}
});
return constants;
},
});
defineLazyObjectProperty(viewConfig, 'Commands', {
get: () => {
const viewManager = NativeModules[viewConfig.Manager];
const commands: {[string]: number} = {};
let index = 0;
viewManager &&
Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
if (typeof value === 'function') {
commands[key] = index++;
}
});
return commands;
},
});
}
}
/**
* Copies the ViewManager constants and commands into UIManager. This is
* only needed for iOS, which puts the constants in the ViewManager
* namespace instead of UIManager, unlike Android.
*/
if (Platform.OS === 'ios') {
Object.keys(getConstants()).forEach(viewName => {
lazifyViewManagerConfig(viewName);
});
} else if (getConstants().ViewManagerNames) {
NativeUIManager.getConstants().ViewManagerNames.forEach(viewManagerName => {
defineLazyObjectProperty(NativeUIManager, viewManagerName, {
get: () =>
nullthrows(NativeUIManager.getConstantsForViewManager)(viewManagerName),
});
});
}
if (!global.nativeCallSyncHook) {
Object.keys(getConstants()).forEach(viewManagerName => {
if (!UIManagerProperties.includes(viewManagerName)) {
if (!viewManagerConfigs[viewManagerName]) {
viewManagerConfigs[viewManagerName] = getConstants()[viewManagerName];
}
defineLazyObjectProperty(NativeUIManager, viewManagerName, {
get: () => {
console.warn(
`Accessing view manager configs directly off UIManager via UIManager['${viewManagerName}'] ` +
`is no longer supported. Use UIManager.getViewManagerConfig('${viewManagerName}') instead.`,
);
return UIManagerJS.getViewManagerConfig(viewManagerName);
},
});
}
});
}
export default UIManagerJS;

View File

@@ -0,0 +1,90 @@
/**
* 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 strict-local
* @format
*/
/**
* This module is meant to be used by the React renderers to create public
* instances and get some data from them (like their instance handle / fiber).
*/
import type {
InternalInstanceHandle,
Node,
ViewConfig,
} from '../../Renderer/shims/ReactNativeTypes';
import type {RootTag} from '../RootTag';
import ReactNativeDocument, {
createReactNativeDocument,
} from '../../../src/private/webapis/dom/nodes/ReactNativeDocument';
import ReactNativeElement from '../../../src/private/webapis/dom/nodes/ReactNativeElement';
import ReadOnlyText from '../../../src/private/webapis/dom/nodes/ReadOnlyText';
import * as RendererProxy from '../../ReactNative/RendererProxy';
export opaque type PublicRootInstance = mixed;
export function createPublicRootInstance(rootTag: RootTag): PublicRootInstance {
// $FlowExpectedError[incompatible-return]
return createReactNativeDocument(rootTag);
}
export function createPublicInstance(
tag: number,
viewConfig: ViewConfig,
internalInstanceHandle: InternalInstanceHandle,
ownerDocument: ReactNativeDocument,
): ReactNativeElement {
return new ReactNativeElement(
tag,
viewConfig,
internalInstanceHandle,
ownerDocument,
);
}
export function createPublicTextInstance(
internalInstanceHandle: InternalInstanceHandle,
ownerDocument: ReactNativeDocument,
): ReadOnlyText {
return new ReadOnlyText(internalInstanceHandle, ownerDocument);
}
export function getNativeTagFromPublicInstance(
publicInstance: ReactNativeElement,
): number {
return publicInstance.__nativeTag;
}
export function getNodeFromPublicInstance(
publicInstance: ReactNativeElement,
): ?Node {
// Avoid loading ReactFabric if using an instance from the legacy renderer.
if (publicInstance.__internalInstanceHandle == null) {
return null;
}
return RendererProxy.getNodeFromInternalInstanceHandle(
// $FlowExpectedError[incompatible-type] __internalInstanceHandle is always an InternalInstanceHandle from React when we get here.
publicInstance.__internalInstanceHandle,
);
}
export function getInternalInstanceHandleFromPublicInstance(
publicInstance: ReactNativeElement,
): InternalInstanceHandle {
// TODO(T174762768): Remove this once OSS versions of renderers will be synced.
// $FlowExpectedError[prop-missing] Keeping this for backwards-compatibility with the renderers versions in open source.
if (publicInstance._internalInstanceHandle != null) {
// $FlowExpectedError[incompatible-type] Keeping this for backwards-compatibility with the renderers versions in open source.
return publicInstance._internalInstanceHandle;
}
// $FlowExpectedError[incompatible-type] __internalInstanceHandle is always an InternalInstanceHandle from React when we get here.
return publicInstance.__internalInstanceHandle;
}

View File

@@ -0,0 +1,38 @@
/**
* 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 strict
* @format
*/
/**
* IMPORTANT!!
*
* This module cannot import `ReactFabric` (directly or indirectly)
* because it can be used by apps only using the legacy renderer.
* In that case `nativeFabricUIManager` isn't defined and `ReactFabric` throws.
*/
export function isPublicInstance(maybeInstance: mixed): boolean {
return (
maybeInstance != null &&
// TODO: implement a better check when the instance is defined in the React Native repository.
(maybeInstance.__nativeTag != null ||
// TODO: remove this check when syncing the new version of the renderer from React to React Native.
isLegacyFabricInstance(maybeInstance))
);
}
function isLegacyFabricInstance(maybeInstance: mixed): boolean {
/* eslint-disable dot-notation */
return (
maybeInstance != null &&
// $FlowExpectedError[incompatible-use]
maybeInstance['_internalInstanceHandle'] != null &&
maybeInstance['_internalInstanceHandle'].stateNode != null &&
maybeInstance['_internalInstanceHandle'].stateNode.canonical != null
);
}

View File

@@ -0,0 +1,511 @@
/**
* 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
* @format
*/
import type {AttributeConfiguration} from '../../Renderer/shims/ReactNativeTypes';
import flattenStyle from '../../StyleSheet/flattenStyle';
import deepDiffer from '../../Utilities/differ/deepDiffer';
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 (Array.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] removedKeys is always non-null
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
const nextProp = nextArray[i];
if (!nextProp) {
continue;
}
updatePayload = addNestedProperty(updatePayload, nextProp, 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 (!Array.isArray(prevProp) && !Array.isArray(nextProp)) {
// Both are leaves, we can diff the leaves.
return diffProperties(updatePayload, prevProp, nextProp, validAttributes);
}
if (Array.isArray(prevProp) && Array.isArray(nextProp)) {
// Both are arrays, we can diff the arrays.
return diffNestedArrayProperty(
updatePayload,
prevProp,
nextProp,
validAttributes,
);
}
if (Array.isArray(prevProp)) {
return diffProperties(
updatePayload,
flattenStyle(prevProp),
nextProp,
validAttributes,
);
}
return diffProperties(
updatePayload,
prevProp,
flattenStyle(nextProp),
validAttributes,
);
}
/**
* 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 (!Array.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];
if (typeof nextProp === 'function') {
const attributeConfigHasProcess =
typeof attributeConfig === 'object' &&
typeof attributeConfig.process === 'function';
if (!attributeConfigHasProcess) {
// functions are converted to booleans as markers that the associated
// events should be sent from native.
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;
}
function addNestedProperty(
payload: null | Object,
props: Object,
validAttributes: AttributeConfiguration,
): null | Object {
// Flatten nested style props.
if (Array.isArray(props)) {
for (let i = 0; i < props.length; i++) {
payload = addNestedProperty(payload, props[i], validAttributes);
}
return payload;
}
for (const propKey in props) {
const prop = props[propKey];
const attributeConfig = ((validAttributes[
propKey
]: any): AttributeConfiguration);
if (attributeConfig == null) {
continue;
}
let newValue;
if (prop === undefined) {
// Discard the prop if it was previously defined.
if (payload && payload[propKey] !== undefined) {
newValue = null;
} else {
continue;
}
} else if (typeof attributeConfig === 'object') {
if (typeof attributeConfig.process === 'function') {
// An atomic prop with custom processing.
newValue = attributeConfig.process(prop);
} else if (typeof attributeConfig.diff === 'function') {
// An atomic prop with custom diffing. We don't need to do diffing when adding props.
newValue = prop;
}
} else {
if (typeof prop === 'function') {
// A function prop. It represents an event handler. Pass it to native as 'true'.
newValue = true;
} else {
// An atomic prop. Doesn't need to be flattened.
newValue = prop;
}
}
if (newValue !== undefined) {
if (!payload) {
payload = ({}: {[string]: $FlowFixMe});
}
payload[propKey] = newValue;
continue;
}
payload = addNestedProperty(payload, prop, attributeConfig);
}
return payload;
}
/**
* 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 {
return diffProperties(updatePayload, prevProps, emptyObject, validAttributes);
}
export function create(
props: Object,
validAttributes: AttributeConfiguration,
): null | Object {
return addNestedProperty(null, props, validAttributes);
}
export function diff(
prevProps: Object,
nextProps: Object,
validAttributes: AttributeConfiguration,
): null | Object {
return diffProperties(
null, // updatePayload
prevProps,
nextProps,
validAttributes,
);
}

View File

@@ -0,0 +1,33 @@
/**
* 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 strict-local
* @format
*/
import type {AttributeConfiguration} from '../../Renderer/shims/ReactNativeTypes';
export default function warnForStyleProps(
props: {...},
validAttributes: AttributeConfiguration,
): void {
if (__DEV__) {
for (const key in validAttributes.style) {
// $FlowFixMe[invalid-computed-prop]
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,
);
}
}
}
}

View File

@@ -0,0 +1,68 @@
/**
* 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 strict-local
* @format
*/
'use strict';
/**
* Perform a set of runtime diagnostics, usually useful for test purpose.
* This is only meaningful for __DEV__ mode.
*/
export type RuntimeDiagnostics = {
/**
* Check if the runtime diagnostics is enabled at all.
*/
isEnabled: () => boolean,
/**
* If enabled, simulate early JavaScript errors during initialization.
*/
simulateEarlyJavaScriptErrors: () => void,
};
export type RuntimeDiagnosticFlag = 'early_js_errors' | 'all';
const flags: Array<RuntimeDiagnosticFlag> = [];
let isEnabled = false;
let shouldEnableAll = false;
if (__DEV__) {
if (typeof global.RN$DiagnosticFlags === 'string') {
global.RN$DiagnosticFlags.split(',').forEach(flag => {
switch (flag) {
case 'early_js_errors':
case 'all':
flags.push(flag);
break;
default:
throw Error(
`RuntimeDiagnosticsFlags: unknown flag was supplied: '${flag}'`,
);
}
});
isEnabled = flags.length > 0;
shouldEnableAll = flags.includes('all');
}
}
const ReactNativeRuntimeDiagnostics: RuntimeDiagnostics = {
isEnabled: () => isEnabled,
simulateEarlyJavaScriptErrors: () => {
if (__DEV__) {
if (!isEnabled) {
return;
}
if (shouldEnableAll || flags.includes('early_js_errors')) {
throw Error('[Runtime Diagnostics] Throwing a JavaScript error.');
}
}
},
};
export default ReactNativeRuntimeDiagnostics;

View File

@@ -0,0 +1,176 @@
/**
* 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 strict-local
* @format
*/
import type {HostInstance} from '../../src/private/types/HostInstance';
import typeof ReactFabricType from '../Renderer/shims/ReactFabric';
import typeof ReactNativeType from '../Renderer/shims/ReactNative';
import type {RootTag} from './RootTag';
import {
onCaughtError,
onRecoverableError,
onUncaughtError,
} from '../../src/private/renderer/errorhandling/ErrorHandlers';
import * as React from 'react';
let cachedFabricRenderer;
let cachedPaperRenderer;
function getFabricRenderer(): ReactFabricType {
if (cachedFabricRenderer == null) {
cachedFabricRenderer = require('../Renderer/shims/ReactFabric').default;
}
return cachedFabricRenderer;
}
function getPaperRenderer(): ReactNativeType {
if (cachedPaperRenderer == null) {
cachedPaperRenderer = require('../Renderer/shims/ReactNative').default;
}
return cachedPaperRenderer;
}
const getMethod: (<MethodName: $Keys<ReactFabricType>>(
() => ReactFabricType,
MethodName,
) => ReactFabricType[MethodName]) &
(<MethodName: $Keys<ReactNativeType>>(
() => ReactNativeType,
MethodName,
) => ReactNativeType[MethodName]) = (getRenderer, methodName) => {
let cachedImpl;
// $FlowExpectedError[incompatible-type]
return function (arg1, arg2, arg3, arg4, arg5, arg6) {
if (cachedImpl == null) {
// $FlowExpectedError[prop-missing]
cachedImpl = getRenderer()[methodName];
}
// $FlowExpectedError[extra-arg]
return cachedImpl(arg1, arg2, arg3, arg4, arg5);
};
};
function getFabricMethod<MethodName: $Keys<ReactFabricType>>(
methodName: MethodName,
): ReactFabricType[MethodName] {
return getMethod(getFabricRenderer, methodName);
}
function getPaperMethod<MethodName: $Keys<ReactNativeType>>(
methodName: MethodName,
): ReactNativeType[MethodName] {
return getMethod(getPaperRenderer, methodName);
}
let cachedFabricRender;
let cachedPaperRender;
export function renderElement({
element,
rootTag,
useFabric,
useConcurrentRoot,
}: {
element: React.MixedElement,
rootTag: number,
useFabric: boolean,
useConcurrentRoot: boolean,
}): void {
if (useFabric) {
if (cachedFabricRender == null) {
cachedFabricRender = getFabricRenderer().render;
}
cachedFabricRender(element, rootTag, null, useConcurrentRoot, {
onCaughtError,
onUncaughtError,
onRecoverableError,
});
} else {
if (cachedPaperRender == null) {
cachedPaperRender = getPaperRenderer().render;
}
cachedPaperRender(element, rootTag, undefined, {
onCaughtError,
onUncaughtError,
onRecoverableError,
});
}
}
let cachedFabricDispatchCommand;
let cachedPaperDispatchCommand;
export function dispatchCommand(
handle: HostInstance,
command: string,
args: Array<mixed>,
): void {
if (global.RN$Bridgeless === true) {
// Note: this function has the same implementation in the legacy and new renderer.
// However, evaluating the old renderer comes with some side effects.
if (cachedFabricDispatchCommand == null) {
cachedFabricDispatchCommand = getFabricRenderer().dispatchCommand;
}
return cachedFabricDispatchCommand(handle, command, args);
} else {
if (cachedPaperDispatchCommand == null) {
cachedPaperDispatchCommand = getPaperRenderer().dispatchCommand;
}
return cachedPaperDispatchCommand(handle, command, args);
}
}
export const findHostInstance_DEPRECATED: <TElementType: React.ElementType>(
// $FlowExpectedError[incompatible-type]
componentOrHandle: ?(React.ElementRef<TElementType> | number),
) => ?HostInstance = getPaperMethod('findHostInstance_DEPRECATED');
export const findNodeHandle: <TElementType: React.ElementType>(
// $FlowExpectedError[incompatible-type]
componentOrHandle: ?(React.ElementRef<TElementType> | number),
) => ?number = getPaperMethod('findNodeHandle');
export const sendAccessibilityEvent: ReactNativeType['sendAccessibilityEvent'] =
getPaperMethod('sendAccessibilityEvent');
/**
* This method is used by AppRegistry to unmount a root when using the old
* React Native renderer (Paper).
*/
export const unmountComponentAtNodeAndRemoveContainer: (
rootTag: RootTag,
) => void =
// $FlowExpectedError[incompatible-type]
getPaperMethod('unmountComponentAtNodeAndRemoveContainer');
export const unstable_batchedUpdates: ReactNativeType['unstable_batchedUpdates'] =
getPaperMethod('unstable_batchedUpdates');
export const isChildPublicInstance: ReactNativeType['isChildPublicInstance'] =
getPaperMethod('isChildPublicInstance');
export const getNodeFromInternalInstanceHandle: ReactFabricType['getNodeFromInternalInstanceHandle'] =
getFabricMethod('getNodeFromInternalInstanceHandle');
export const getPublicInstanceFromInternalInstanceHandle: ReactFabricType['getPublicInstanceFromInternalInstanceHandle'] =
getFabricMethod('getPublicInstanceFromInternalInstanceHandle');
export const getPublicInstanceFromRootTag: ReactFabricType['getPublicInstanceFromRootTag'] =
getFabricMethod('getPublicInstanceFromRootTag');
export function isProfilingRenderer(): boolean {
return Boolean(__DEV__);
}

View File

@@ -0,0 +1,20 @@
/**
* 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.
*
* @format
*/
import type * as React from 'react';
export type NodeHandle = number;
export function findNodeHandle(
componentOrHandle:
| null
| number
| React.Component<any, any>
| React.ComponentClass<any>,
): null | NodeHandle;

View File

@@ -0,0 +1,26 @@
/**
* 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 strict-local
* @format
*/
/**
* This module exists to allow apps to select their renderer implementation
* (e.g.: Fabric-only, Paper-only) without having to pull all the renderer
* implementations into their app bundle, which affects app size.
*
* By default, the setup will be:
* -> RendererProxy
* -> RendererImplementation (which uses Fabric or Paper depending on a flag at runtime)
*
* But this will allow a setup like this without duplicating logic:
* -> RendererProxy (fork)
* -> RendererImplementation (which uses Fabric or Paper depending on a flag at runtime)
* or -> OtherImplementation (which uses Fabric only)
*/
export * from './RendererImplementation';

View File

@@ -0,0 +1,13 @@
/**
* 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.
*
* @format
*/
import type * as React from 'react';
export type RootTag = number;
export const RootTagContext: React.Context<RootTag>;

View File

@@ -0,0 +1,27 @@
/**
* 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 strict
* @format
*/
import * as React from 'react';
import {createContext} from 'react';
export opaque type RootTag = number;
export const RootTagContext: React.Context<RootTag> = createContext<RootTag>(0);
if (__DEV__) {
RootTagContext.displayName = 'RootTagContext';
}
/**
* Intended to only be used by `AppContainer`.
*/
export function createRootTag(rootTag: number | RootTag): RootTag {
return rootTag;
}

View File

@@ -0,0 +1,114 @@
/**
* 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.
*
* @format
*/
import type * as React from 'react';
import {
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
} from '../../types/public/ReactNativeTypes';
export interface UIManagerStatic {
/**
* Determines the location on screen, width, and height of the given view and
* returns the values via an async callback. If successful, the callback will
* be called with the following arguments:
*
* - x
* - y
* - width
* - height
* - pageX
* - pageY
*
* Note that these measurements are not available until after the rendering
* has been completed in native. If you need the measurements as soon as
* possible, consider using the [`onLayout`
* prop](docs/view.html#onlayout) instead.
*
* @deprecated Use `ref.measure` instead.
*/
measure(node: number, callback: MeasureOnSuccessCallback): void;
/**
* Determines the location of the given view in the window and returns the
* values via an async callback. If the React root view is embedded in
* another native view, this will give you the absolute coordinates. If
* successful, the callback will be called with the following
* arguments:
*
* - x
* - y
* - width
* - height
*
* Note that these measurements are not available until after the rendering
* has been completed in native.
*
* @deprecated Use `ref.measureInWindow` instead.
*/
measureInWindow(
node: number,
callback: MeasureInWindowOnSuccessCallback,
): void;
/**
* Like [`measure()`](#measure), but measures the view relative an ancestor,
* specified as `relativeToNativeNode`. This means that the returned x, y
* are relative to the origin x, y of the ancestor view.
*
* As always, to obtain a native node handle for a component, you can use
* `React.findNodeHandle(component)`.
*
* @deprecated Use `ref.measureLayout` instead.
*/
measureLayout(
node: number,
relativeToNativeNode: number,
onFail: () => void /* currently unused */,
onSuccess: MeasureLayoutOnSuccessCallback,
): void;
/**
* Automatically animates views to their new positions when the
* next layout happens.
*
* A common way to use this API is to call it before calling `setState`.
*
* Note that in order to get this to work on **Android** you need to set the following flags via `UIManager`:
*
* UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
*/
setLayoutAnimationEnabledExperimental?:
| ((value: boolean) => void)
| undefined;
getViewManagerConfig: (name: string) => {
Commands: {[key: string]: number};
};
hasViewManagerConfig: (name: string) => boolean;
/**
* Used to call a native view method from JavaScript
*
* reactTag - Id of react view.
* commandID - Id of the native method that should be called.
* commandArgs - Args of the native method that we can pass from JS to native.
*/
dispatchViewManagerCommand: (
reactTag: number | null,
commandID: number | string,
commandArgs?: Array<any>,
) => void;
}
export const UIManager: UIManagerStatic;
export type UIManager = UIManagerStatic;
export default UIManager;

View File

@@ -0,0 +1,193 @@
/**
* 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
* @format
*/
import type {UIManagerJSInterface} from '../Types/UIManagerJSInterface';
import {getFabricUIManager} from './FabricUIManager';
import nullthrows from 'nullthrows';
function isFabricReactTag(reactTag: number): boolean {
// React reserves even numbers for Fabric.
return reactTag % 2 === 0;
}
const UIManagerImpl: UIManagerJSInterface =
global.RN$Bridgeless === true
? require('./BridgelessUIManager').default
: require('./PaperUIManager').default;
// $FlowFixMe[cannot-spread-interface]
const UIManager: UIManagerJSInterface = {
...UIManagerImpl,
measure(
reactTag: number,
callback: (
left: number,
top: number,
width: number,
height: number,
pageX: number,
pageY: number,
) => void,
): void {
if (isFabricReactTag(reactTag)) {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (shadowNode) {
FabricUIManager.measure(shadowNode, callback);
} else {
console.warn(`measure cannot find view with tag #${reactTag}`);
// $FlowFixMe[incompatible-type]
callback();
}
} else {
// Paper
UIManagerImpl.measure(reactTag, callback);
}
},
measureInWindow(
reactTag: number,
callback: (
left: number,
top: number,
width: number,
height: number,
) => void,
): void {
if (isFabricReactTag(reactTag)) {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (shadowNode) {
FabricUIManager.measureInWindow(shadowNode, callback);
} else {
console.warn(`measure cannot find view with tag #${reactTag}`);
// $FlowFixMe[incompatible-type]
callback();
}
} else {
// Paper
UIManagerImpl.measureInWindow(reactTag, callback);
}
},
measureLayout(
reactTag: number,
ancestorReactTag: number,
errorCallback: (error: Object) => void,
callback: (
left: number,
top: number,
width: number,
height: number,
) => void,
): void {
if (isFabricReactTag(reactTag)) {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
const ancestorShadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(ancestorReactTag);
if (!shadowNode || !ancestorShadowNode) {
return;
}
FabricUIManager.measureLayout(
shadowNode,
ancestorShadowNode,
errorCallback,
callback,
);
} else {
// Paper
UIManagerImpl.measureLayout(
reactTag,
ancestorReactTag,
errorCallback,
callback,
);
}
},
measureLayoutRelativeToParent(
reactTag: number,
errorCallback: (error: Object) => void,
callback: (
left: number,
top: number,
width: number,
height: number,
) => void,
): void {
if (isFabricReactTag(reactTag)) {
console.warn(
'RCTUIManager.measureLayoutRelativeToParent method is deprecated and it will not be implemented in newer versions of RN (Fabric) - T47686450',
);
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (shadowNode) {
FabricUIManager.measure(
shadowNode,
(left, top, width, height, pageX, pageY) => {
callback(left, top, width, height);
},
);
}
} else {
// Paper
UIManagerImpl.measureLayoutRelativeToParent(
reactTag,
errorCallback,
callback,
);
}
},
dispatchViewManagerCommand(
reactTag: number,
commandName: number | string,
commandArgs: any[],
) {
// Sometimes, libraries directly pass in the output of `findNodeHandle` to
// this function without checking if it's null. This guards against that
// case. We throw early here in Javascript so we can get a JS stacktrace
// instead of a harder-to-debug native Java or Objective-C stacktrace.
if (typeof reactTag !== 'number') {
throw new Error('dispatchViewManagerCommand: found null reactTag');
}
if (isFabricReactTag(reactTag)) {
const FabricUIManager = nullthrows(getFabricUIManager());
const shadowNode =
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
if (shadowNode) {
// Transform the accidental CommandID into a CommandName which is the stringified number.
// The interop layer knows how to convert this number into the right method name.
// Stringify a string is a no-op, so it's safe.
commandName = `${commandName}`;
FabricUIManager.dispatchCommand(shadowNode, commandName, commandArgs);
}
} else {
UIManagerImpl.dispatchViewManagerCommand(
reactTag,
// We have some legacy components that are actually already using strings. ¯\_(ツ)_/¯
// $FlowFixMe[incompatible-type]
commandName,
commandArgs,
);
}
},
};
export default UIManager;

View File

@@ -0,0 +1,63 @@
/**
* 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 strict
* @format
*/
'use strict';
/**
* The list of non-ViewManager related UIManager properties.
*
* In an effort to improve startup performance by lazily loading view managers,
* the interface to access view managers will change from
* UIManager['viewManagerName'] to UIManager.getViewManagerConfig('viewManagerName').
* By using a function call instead of a property access, the UIManager will
* be able to initialize and load the required view manager from native
* synchronously. All of React Native's core components have been updated to
* use getViewManagerConfig(). For the next few releases, any usage of
* UIManager['viewManagerName'] will result in a warning. Because React Native
* does not support Proxy objects, a view manager access is implied if any of
* UIManager's properties that are not one of the properties below is being
* accessed. Once UIManager property accesses for view managers has been fully
* deprecated, this file will also be removed.
*/
const UIManagerProperties: $ReadOnlyArray<string> = [
'clearJSResponder',
'configureNextLayoutAnimation',
'createView',
'dispatchViewManagerCommand',
'findSubviewIn',
'getConstantsForViewManager',
'getDefaultEventTypes',
'manageChildren',
'measure',
'measureInWindow',
'measureLayout',
'measureLayoutRelativeToParent',
'removeRootView',
'sendAccessibilityEvent',
'setChildren',
'setJSResponder',
'setLayoutAnimationEnabledExperimental',
'updateView',
'viewIsDescendantOf',
'LazyViewManagersEnabled',
'ViewManagerNames',
'StyleConstants',
'AccessibilityEventTypes',
'UIView',
'getViewManagerConfig',
'hasViewManagerConfig',
'blur',
'focus',
'genericBubblingEventTypes',
'genericDirectEventTypes',
'lazilyLoadView',
];
export default UIManagerProperties;

View File

@@ -0,0 +1,33 @@
/**
* 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 strict
* @format
*/
import * as React from 'react';
type NoopComponent = component(children: React.Node);
const cache: Map<
string, // displayName
NoopComponent, // ComponentWithDisplayName
> = new Map();
export default function getCachedComponentWithDisplayName(
displayName: string,
): NoopComponent {
let ComponentWithDisplayName = cache.get(displayName);
if (!ComponentWithDisplayName) {
ComponentWithDisplayName = ({children}: {children: React.Node}) => children;
// $FlowFixMe[prop-missing]
ComponentWithDisplayName.displayName = displayName;
cache.set(displayName, ComponentWithDisplayName);
}
return ComponentWithDisplayName;
}

View File

@@ -0,0 +1,226 @@
/**
* 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
* @format
*/
'use strict';
import processBoxShadow from '../StyleSheet/processBoxShadow';
const ReactNativeStyleAttributes =
require('../Components/View/ReactNativeStyleAttributes').default;
const resolveAssetSource = require('../Image/resolveAssetSource').default;
const processBackgroundImage =
require('../StyleSheet/processBackgroundImage').default;
const processBackgroundPosition =
require('../StyleSheet/processBackgroundPosition').default;
const processBackgroundRepeat =
require('../StyleSheet/processBackgroundRepeat').default;
const processBackgroundSize =
require('../StyleSheet/processBackgroundSize').default;
const processColor = require('../StyleSheet/processColor').default;
const processColorArray = require('../StyleSheet/processColorArray').default;
const processFilter = require('../StyleSheet/processFilter').default;
const insetsDiffer = require('../Utilities/differ/insetsDiffer').default;
const matricesDiffer = require('../Utilities/differ/matricesDiffer').default;
const pointsDiffer = require('../Utilities/differ/pointsDiffer').default;
const sizesDiffer = require('../Utilities/differ/sizesDiffer').default;
const UIManager = require('./UIManager').default;
const nullthrows = require('nullthrows');
function getNativeComponentAttributes(uiViewClassName: string): any {
const viewConfig = UIManager.getViewManagerConfig(uiViewClassName);
if (viewConfig == null) {
return null;
}
// TODO: This seems like a whole lot of runtime initialization for every
// native component that can be either avoided or simplified.
let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig;
let nativeProps = viewConfig.NativeProps;
bubblingEventTypes = bubblingEventTypes ?? {};
directEventTypes = directEventTypes ?? {};
while (baseModuleName) {
const baseModule = UIManager.getViewManagerConfig(baseModuleName);
if (!baseModule) {
baseModuleName = null;
} else {
bubblingEventTypes = {
...baseModule.bubblingEventTypes,
...bubblingEventTypes,
};
directEventTypes = {
...baseModule.directEventTypes,
...directEventTypes,
};
nativeProps = {
...baseModule.NativeProps,
...nativeProps,
};
baseModuleName = baseModule.baseModuleName;
}
}
const validAttributes: {[string]: mixed} = {};
for (const key in nativeProps) {
const typeName = nativeProps[key];
const diff = getDifferForType(typeName);
const process = getProcessorForType(typeName);
// If diff or process == null, omit the corresponding property from the Attribute
// Why:
// 1. Consistency with AttributeType flow type
// 2. Consistency with Static View Configs, which omit the null properties
validAttributes[key] =
diff == null
? process == null
? true
: {process}
: process == null
? {diff}
: {diff, process};
}
// Unfortunately, the current setup declares style properties as top-level
// props. This makes it so we allow style properties in the `style` prop.
// TODO: Move style properties into a `style` prop and disallow them as
// top-level props on the native side.
validAttributes.style = ReactNativeStyleAttributes;
// $FlowFixMe[unsafe-object-assign]
Object.assign(viewConfig, {
uiViewClassName,
validAttributes,
bubblingEventTypes,
directEventTypes,
});
attachDefaultEventTypes(viewConfig);
return viewConfig;
}
function attachDefaultEventTypes(viewConfig: any) {
// This is supported on UIManager platforms (ex: Android),
// as lazy view managers are not implemented for all platforms.
// See [UIManager] for details on constants and implementations.
const constants = UIManager.getConstants();
if (constants.ViewManagerNames || constants.LazyViewManagersEnabled) {
// Lazy view managers enabled.
viewConfig = merge(
viewConfig,
nullthrows(UIManager.getDefaultEventTypes)(),
);
} else {
viewConfig.bubblingEventTypes = merge(
viewConfig.bubblingEventTypes,
constants.genericBubblingEventTypes,
);
viewConfig.directEventTypes = merge(
viewConfig.directEventTypes,
constants.genericDirectEventTypes,
);
}
}
// TODO: Figure out how to avoid all this runtime initialization cost.
function merge(destination: ?Object, source: ?Object): ?Object {
if (!source) {
return destination;
}
if (!destination) {
return source;
}
for (const key in source) {
if (!source.hasOwnProperty(key)) {
continue;
}
let sourceValue = source[key];
if (destination.hasOwnProperty(key)) {
const destinationValue = destination[key];
if (
typeof sourceValue === 'object' &&
typeof destinationValue === 'object'
) {
sourceValue = merge(destinationValue, sourceValue);
}
}
destination[key] = sourceValue;
}
return destination;
}
function getDifferForType(
typeName: string,
): ?(prevProp: any, nextProp: any) => boolean {
switch (typeName) {
// iOS Types
case 'CATransform3D':
return matricesDiffer;
case 'CGPoint':
return pointsDiffer;
case 'CGSize':
return sizesDiffer;
case 'UIEdgeInsets':
return insetsDiffer;
// Android Types
case 'Point':
return pointsDiffer;
case 'EdgeInsets':
return insetsDiffer;
}
return null;
}
function getProcessorForType(typeName: string): ?(nextProp: any) => any {
switch (typeName) {
// iOS Types
case 'CGColor':
case 'UIColor':
return processColor;
case 'CGColorArray':
case 'UIColorArray':
return processColorArray;
case 'CGImage':
case 'UIImage':
case 'RCTImageSource':
return resolveAssetSource;
case 'BoxShadowArray':
return processBoxShadow;
case 'FilterArray':
return processFilter;
// Android Types
case 'Color':
return processColor;
case 'ColorArray':
return processColorArray;
case 'Filter':
return processFilter;
case 'BackgroundImage':
return processBackgroundImage;
case 'BackgroundPosition':
return processBackgroundPosition;
case 'BackgroundRepeat':
return processBackgroundRepeat;
case 'BackgroundSize':
return processBackgroundSize;
case 'ImageSource':
return resolveAssetSource;
case 'BoxShadow':
return processBoxShadow;
}
return null;
}
export default getNativeComponentAttributes;

View File

@@ -0,0 +1,118 @@
/**
* 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
* @format
*/
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
import GlobalPerformanceLogger from '../Utilities/GlobalPerformanceLogger';
import PerformanceLoggerContext from '../Utilities/PerformanceLoggerContext';
import warnOnce from '../Utilities/warnOnce';
import AppContainer from './AppContainer';
import DisplayMode, {type DisplayModeType} from './DisplayMode';
import getCachedComponentWithDebugName from './getCachedComponentWithDebugName';
import * as Renderer from './RendererProxy';
import invariant from 'invariant';
import * as React from 'react';
// require BackHandler so it sets the default handler that exits the app if no listeners respond
import '../Utilities/BackHandler';
type ActivityType = component(
...{
mode: 'visible' | 'hidden',
children: React.Node,
}
);
export default function renderApplication<Props: Object>(
RootComponent: React.ComponentType<Props>,
initialProps: Props,
rootTag: any,
WrapperComponent?: ?React.ComponentType<any>,
rootViewStyle?: ?ViewStyleProp,
fabric?: boolean,
scopedPerformanceLogger?: IPerformanceLogger,
isLogBox?: boolean,
debugName?: string,
displayMode?: ?DisplayModeType,
useOffscreen?: boolean,
) {
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
const performanceLogger = scopedPerformanceLogger ?? GlobalPerformanceLogger;
let renderable: React.MixedElement = (
<PerformanceLoggerContext.Provider value={performanceLogger}>
<AppContainer
rootTag={rootTag}
fabric={fabric}
WrapperComponent={WrapperComponent}
rootViewStyle={rootViewStyle}
initialProps={initialProps ?? Object.freeze({})}
internal_excludeLogBox={isLogBox}>
<RootComponent {...initialProps} rootTag={rootTag} />
</AppContainer>
</PerformanceLoggerContext.Provider>
);
if (__DEV__ && debugName) {
const RootComponentWithMeaningfulName = getCachedComponentWithDebugName(
`${debugName}(RootComponent)`,
);
renderable = (
<RootComponentWithMeaningfulName>
{renderable}
</RootComponentWithMeaningfulName>
);
}
if (useOffscreen && displayMode != null) {
// $FlowFixMe[incompatible-type]
// $FlowFixMe[prop-missing]
// $FlowFixMe[missing-export]
const Activity: ActivityType = React.unstable_Activity;
renderable = (
<Activity
mode={displayMode === DisplayMode.VISIBLE ? 'visible' : 'hidden'}>
{renderable}
</Activity>
);
}
// We want to have concurrentRoot always enabled when you're on Fabric.
const useConcurrentRoot = Boolean(fabric);
performanceLogger.startTimespan('renderApplication_React_render');
performanceLogger.setExtra(
'usedReactConcurrentRoot',
useConcurrentRoot ? '1' : '0',
);
performanceLogger.setExtra('usedReactFabric', fabric ? '1' : '0');
performanceLogger.setExtra(
'usedReactProfiler',
Renderer.isProfilingRenderer(),
);
Renderer.renderElement({
element: renderable,
rootTag,
useFabric: Boolean(fabric),
useConcurrentRoot,
});
const newArchitecture = !!fabric;
if (!newArchitecture) {
warnOnce(
'[OSS][OldArchDeprecatedWarning]',
'The app is running using the Legacy Architecture. The Legacy Architecture is deprecated and will be removed in a future version of React Native. Please consider migrating to the New Architecture. For more information, please see https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here',
);
}
performanceLogger.stopTimespan('renderApplication_React_render');
}

View File

@@ -0,0 +1,23 @@
/**
* 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.
*
* @format
*/
import {HostComponent} from '../../types/public/ReactNativeTypes';
/**
* Creates values that can be used like React components which represent native
* view managers. You should create JavaScript modules that wrap these values so
* that the results are memoized. Example:
*
* const View = requireNativeComponent('RCTView');
*
* The concrete return type of `requireNativeComponent` is a string, but the declared type is
* `HostComponent` because TypeScript assumes anonymous JSX intrinsics (e.g. a `string`) not
* to have any props.
*/
export function requireNativeComponent<T>(viewName: string): HostComponent<T>;

View File

@@ -0,0 +1,36 @@
/**
* 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
* @format
*/
'use strict';
import type {HostComponent} from '../../src/private/types/HostComponent';
const createReactNativeComponentClass =
require('../Renderer/shims/createReactNativeComponentClass').default;
const getNativeComponentAttributes =
require('./getNativeComponentAttributes').default;
/**
* Creates values that can be used like React components which represent native
* view managers. You should create JavaScript modules that wrap these values so
* that the results are memoized. Example:
*
* const View = requireNativeComponent('RCTView');
*
*/
const requireNativeComponent = <T: {...}>(
uiViewClassName: string,
): HostComponent<T> =>
((createReactNativeComponentClass(uiViewClassName, () =>
getNativeComponentAttributes(uiViewClassName),
): any): HostComponent<T>);
export default requireNativeComponent;