Skip to content

Commit

Permalink
Extract use of renderer to its own module
Browse files Browse the repository at this point in the history
Summary:
This diff creates a proxy module to interact with the React Native renderer. The goal of this proxy is to decouple usages of several functions (e.g.: `findNodeHandle`, etc.) from the actual renderer used in an app. This way, we can easily switch between renderers without having to change code depending on it.

This will be useful to remove a specific renderer from an app bundle when it's no longer used (e.g.: Paper on the Facebook App).

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D39205975

fbshipit-source-id: 05289c0c3c8cd26d81aa1d2163097c73ec40c6ad
  • Loading branch information
rubennorte authored and facebook-github-bot committed Sep 8, 2022
1 parent 5efb4f0 commit b06cae3
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 43 deletions.
25 changes: 11 additions & 14 deletions Libraries/ReactNative/AppRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,20 @@
* @format
*/

const BatchedBridge = require('../BatchedBridge/BatchedBridge');
const BugReporting = require('../BugReporting/BugReporting');
const ReactNative = require('../Renderer/shims/ReactNative');
const SceneTracker = require('../Utilities/SceneTracker');

const infoLog = require('../Utilities/infoLog');
const invariant = require('invariant');
const renderApplication = require('./renderApplication');
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
import type {RootTag} from 'react-native/Libraries/Types/RootTagTypes';

import {coerceDisplayMode} from './DisplayMode';
import BatchedBridge from '../BatchedBridge/BatchedBridge';
import BugReporting from '../BugReporting/BugReporting';
import createPerformanceLogger from '../Utilities/createPerformanceLogger';
import NativeHeadlessJsTaskSupport from './NativeHeadlessJsTaskSupport';
import infoLog from '../Utilities/infoLog';
import SceneTracker from '../Utilities/SceneTracker';
import {coerceDisplayMode} from './DisplayMode';
import HeadlessJsTaskError from './HeadlessJsTaskError';
import type {RootTag} from 'react-native/Libraries/Types/RootTagTypes';
import NativeHeadlessJsTaskSupport from './NativeHeadlessJsTaskSupport';
import {unmountComponentAtNodeAndRemoveContainer} from './RendererProxy';
import renderApplication from './renderApplication';
import invariant from 'invariant';

type Task = (taskData: any) => Promise<void>;
export type TaskProvider = () => Task;
Expand Down Expand Up @@ -250,9 +249,7 @@ const AppRegistry = {
* See https://reactnative.dev/docs/appregistry#unmountapplicationcomponentatroottag
*/
unmountApplicationComponentAtRootTag(rootTag: RootTag): void {
// NOTE: RootTag type
// $FlowFixMe[incompatible-call] RootTag: RootTag is incompatible with number, needs an updated synced version of the ReactNativeTypes.js file
ReactNative.unmountComponentAtNodeAndRemoveContainer(rootTag);
unmountComponentAtNodeAndRemoveContainer(rootTag);
},

/**
Expand Down
111 changes: 111 additions & 0 deletions Libraries/ReactNative/RendererImplementation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* 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
* @flow strict-local
*/

import {type RootTag} from './RootTag';
import type {Element, ElementType, ElementRef} from 'react';
import type {HostComponent} from '../Renderer/shims/ReactNativeTypes';

export function renderElement({
element,
rootTag,
useFabric,
useConcurrentRoot,
}: {
element: Element<ElementType>,
rootTag: number,
useFabric: boolean,
useConcurrentRoot: boolean,
}): void {
if (useFabric) {
require('../Renderer/shims/ReactFabric').render(
element,
rootTag,
null,
useConcurrentRoot,
);
} else {
require('../Renderer/shims/ReactNative').render(element, rootTag);
}
}

export function findHostInstance_DEPRECATED<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?ElementRef<HostComponent<mixed>> {
return require('../Renderer/shims/ReactNative').findHostInstance_DEPRECATED(
componentOrHandle,
);
}

export function findNodeHandle<TElementType: ElementType>(
componentOrHandle: ?(ElementRef<TElementType> | number),
): ?number {
return require('../Renderer/shims/ReactNative').findNodeHandle(
componentOrHandle,
);
}

export function dispatchCommand(
handle: ElementRef<HostComponent<mixed>>,
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.
return require('../Renderer/shims/ReactFabric').dispatchCommand(
handle,
command,
args,
);
} else {
return require('../Renderer/shims/ReactNative').dispatchCommand(
handle,
command,
args,
);
}
}

export function sendAccessibilityEvent(
handle: ElementRef<HostComponent<mixed>>,
eventType: string,
): void {
return require('../Renderer/shims/ReactNative').sendAccessibilityEvent(
handle,
eventType,
);
}

/**
* This method is used by AppRegistry to unmount a root when using the old
* React Native renderer (Paper).
*/
export function unmountComponentAtNodeAndRemoveContainer(rootTag: RootTag) {
// $FlowExpectedError[incompatible-type] rootTag is an opaque type so we can't really cast it as is.
const rootTagAsNumber: number = rootTag;
require('../Renderer/shims/ReactNative').unmountComponentAtNodeAndRemoveContainer(
rootTagAsNumber,
);
}

export function unstable_batchedUpdates<T>(
fn: T => void,
bookkeeping: T,
): void {
// This doesn't actually do anything when batching updates for a Fabric root.
return require('../Renderer/shims/ReactNative').unstable_batchedUpdates(
fn,
bookkeeping,
);
}

export function isProfilingRenderer(): boolean {
return Boolean(__DEV__);
}
26 changes: 26 additions & 0 deletions Libraries/ReactNative/RendererProxy.js
Original file line number Diff line number Diff line change
@@ -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.
*
* @format
* @flow strict-local
*/

/**
* 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';
39 changes: 19 additions & 20 deletions Libraries/ReactNative/renderApplication.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
* @flow
*/

const AppContainer = require('./AppContainer');
import GlobalPerformanceLogger from '../Utilities/GlobalPerformanceLogger';
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
import PerformanceLoggerContext from '../Utilities/PerformanceLoggerContext';
import type {DisplayModeType} from './DisplayMode';
import getCachedComponentWithDebugName from './getCachedComponentWithDebugName';
const React = require('react');

const invariant = require('invariant');
import GlobalPerformanceLogger from '../Utilities/GlobalPerformanceLogger';
import PerformanceLoggerContext from '../Utilities/PerformanceLoggerContext';
import AppContainer from './AppContainer';
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
require('../Utilities/BackHandler');
import '../Utilities/BackHandler';

function renderApplication<Props: Object>(
export default function renderApplication<Props: Object>(
RootComponent: React.ComponentType<Props>,
initialProps: Props,
rootTag: any,
Expand Down Expand Up @@ -69,17 +70,15 @@ function renderApplication<Props: Object>(
useConcurrentRoot ? '1' : '0',
);
performanceLogger.setExtra('usedReactFabric', fabric ? '1' : '0');
if (fabric) {
require('../Renderer/shims/ReactFabric').render(
renderable,
rootTag,
null,
useConcurrentRoot,
);
} else {
require('../Renderer/shims/ReactNative').render(renderable, rootTag);
}
performanceLogger.setExtra(
'usedReactProfiler',
Renderer.isProfilingRenderer(),
);
Renderer.renderElement({
element: renderable,
rootTag,
useFabric: Boolean(fabric),
useConcurrentRoot: Boolean(useConcurrentRoot),
});
performanceLogger.stopTimespan('renderApplication_React_render');
}

module.exports = renderApplication;
13 changes: 6 additions & 7 deletions ReactAndroid/src/androidTest/js/UIManagerTestModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@

'use strict';

const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
const React = require('react');

const renderApplication = require('react-native/Libraries/ReactNative/renderApplication');

const {StyleSheet, Text, View} = require('react-native');
import type {RootTag} from 'react-native/Libraries/Types/RootTagTypes';

import * as React from 'react';
import {StyleSheet, Text, View} from 'react-native';
import BatchedBridge from 'react-native/Libraries/BatchedBridge/BatchedBridge';
import renderApplication from 'react-native/Libraries/ReactNative/renderApplication';

type FlexTestAppProps = $ReadOnly<{||}>;
class FlexTestApp extends React.Component<FlexTestAppProps> {
render(): React.Node {
Expand Down Expand Up @@ -267,4 +266,4 @@ BatchedBridge.registerCallableModule(
UIManagerTestModule,
);

module.exports = UIManagerTestModule;
export default UIManagerTestModule;
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ module.exports = {
return require('./Libraries/Animated/Easing');
},
get findNodeHandle(): $PropertyType<ReactNative, 'findNodeHandle'> {
return require('./Libraries/Renderer/shims/ReactNative').findNodeHandle;
return require('./Libraries/ReactNative/RendererProxy').findNodeHandle;
},
get I18nManager(): I18nManager {
return require('./Libraries/ReactNative/I18nManager');
Expand Down Expand Up @@ -366,7 +366,7 @@ module.exports = {
ReactNative,
'unstable_batchedUpdates',
> {
return require('./Libraries/Renderer/shims/ReactNative')
return require('./Libraries/ReactNative/RendererProxy')
.unstable_batchedUpdates;
},
get useColorScheme(): useColorScheme {
Expand Down

0 comments on commit b06cae3

Please sign in to comment.