From b99d0b14160150c566e091bd10b634beec9a58c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Mon, 9 Apr 2018 20:05:57 -0700 Subject: [PATCH] [RN] Move view config registry to shims (#12569) * Move view config registry to shims This ensures that both Fabric and RN renderers share the same view config registry since it is stateful. I had to duplicate in the mocks for testing. * Move createReactNativeComponentClass to shims and delete internal usage Since createReactNativeComponentClass is just an alias for the register there's no need to bundle it. This file should probably just move back to RN too. --- .../react-native-renderer/src/ReactFabric.js | 2 - .../src/ReactFabricRenderer.js | 2 +- .../src/ReactNativeBridgeEventPlugin.js | 2 +- .../src/ReactNativeFiberRenderer.js | 2 +- .../src/ReactNativeRenderer.js | 2 - .../src/ReactNativeTypes.js | 4 - .../ReactNativeViewConfigRegistry.js | 21 ++-- .../src/__mocks__/View.js | 18 --- .../__tests__/ReactFabric-test.internal.js | 4 +- .../ReactFabricAndNative-test.internal.js | 4 +- .../ReactNativeError-test.internal.js | 4 +- .../ReactNativeEvents-test.internal.js | 4 +- .../ReactNativeMount-test.internal.js | 4 +- ...ReactNativeComponentClass-test.internal.js | 4 +- .../src/createReactNativeComponentClass.js | 29 ----- scripts/flow/react-native-host-hooks.js | 17 ++- scripts/rollup/bundles.js | 4 +- .../ReactNativeViewConfigRegistry.js | 106 ++++++++++++++++++ .../createReactNativeComponentClass.js | 24 +++- 19 files changed, 166 insertions(+), 91 deletions(-) rename packages/react-native-renderer/src/{ => __mocks__}/ReactNativeViewConfigRegistry.js (84%) delete mode 100644 packages/react-native-renderer/src/__mocks__/View.js delete mode 100644 packages/react-native-renderer/src/createReactNativeComponentClass.js create mode 100644 scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index fa406703fce79..edd6047270989 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -21,7 +21,6 @@ import ReactNativeComponent from './ReactNativeComponent'; import * as ReactNativeComponentTree from './ReactNativeComponentTree'; import ReactFabricRenderer from './ReactFabricRenderer'; import {getInspectorDataForViewTag} from './ReactNativeFiberInspector'; -import createReactNativeComponentClass from './createReactNativeComponentClass'; import {injectFindHostInstance} from './findNodeHandle'; import findNumericNodeHandle from './findNumericNodeHandle'; @@ -73,7 +72,6 @@ const ReactFabric: ReactFabricType = { NativeMethodsMixin, // Used by react-native-github/Libraries/ components ReactNativeComponentTree, // ScrollResponder - createReactNativeComponentClass, // RCTText, RCTView, ReactNativeART }, }; diff --git a/packages/react-native-renderer/src/ReactFabricRenderer.js b/packages/react-native-renderer/src/ReactFabricRenderer.js index 007c3772e5807..0ec040bddf05b 100644 --- a/packages/react-native-renderer/src/ReactFabricRenderer.js +++ b/packages/react-native-renderer/src/ReactFabricRenderer.js @@ -18,7 +18,7 @@ import type { import {mountSafeCallback, warnForStyleProps} from './NativeMethodsMixinUtils'; import * as ReactNativeAttributePayload from './ReactNativeAttributePayload'; import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling'; -import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry'; +import * as ReactNativeViewConfigRegistry from 'ReactNativeViewConfigRegistry'; import ReactFiberReconciler from 'react-reconciler'; import deepFreezeAndThrowOnMutationInDev from 'deepFreezeAndThrowOnMutationInDev'; diff --git a/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js b/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js index 19f6938d52648..69bc7a260a558 100644 --- a/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js +++ b/packages/react-native-renderer/src/ReactNativeBridgeEventPlugin.js @@ -12,7 +12,7 @@ import { accumulateTwoPhaseDispatches, accumulateDirectDispatches, } from 'events/EventPropagators'; -import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry'; +import * as ReactNativeViewConfigRegistry from 'ReactNativeViewConfigRegistry'; import SyntheticEvent from 'events/SyntheticEvent'; import invariant from 'fbjs/lib/invariant'; diff --git a/packages/react-native-renderer/src/ReactNativeFiberRenderer.js b/packages/react-native-renderer/src/ReactNativeFiberRenderer.js index ebb4e17d1f09a..7de768b3b2981 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeFiberRenderer.js @@ -16,7 +16,7 @@ import invariant from 'fbjs/lib/invariant'; import UIManager from 'UIManager'; import deepFreezeAndThrowOnMutationInDev from 'deepFreezeAndThrowOnMutationInDev'; -import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry'; +import * as ReactNativeViewConfigRegistry from 'ReactNativeViewConfigRegistry'; import * as ReactNativeAttributePayload from './ReactNativeAttributePayload'; import { precacheFiberNode, diff --git a/packages/react-native-renderer/src/ReactNativeRenderer.js b/packages/react-native-renderer/src/ReactNativeRenderer.js index 6419ef9ca1d96..faef67d42993b 100644 --- a/packages/react-native-renderer/src/ReactNativeRenderer.js +++ b/packages/react-native-renderer/src/ReactNativeRenderer.js @@ -25,7 +25,6 @@ import ReactNativeComponent from './ReactNativeComponent'; import * as ReactNativeComponentTree from './ReactNativeComponentTree'; import ReactNativeFiberRenderer from './ReactNativeFiberRenderer'; import {getInspectorDataForViewTag} from './ReactNativeFiberInspector'; -import createReactNativeComponentClass from './createReactNativeComponentClass'; import {injectFindHostInstance} from './findNodeHandle'; import findNumericNodeHandle from './findNumericNodeHandle'; @@ -98,7 +97,6 @@ const ReactNativeRenderer: ReactNativeType = { NativeMethodsMixin, // Used by react-native-github/Libraries/ components ReactNativeComponentTree, // ScrollResponder - createReactNativeComponentClass, // RCTText, RCTView, ReactNativeART computeComponentStackForErrorReporting, }, }; diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 8f9305d0358a6..fdee974ae88aa 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -71,10 +71,6 @@ export type NativeMethodsMixinType = { type SecretInternalsType = { NativeMethodsMixin: NativeMethodsMixinType, - createReactNativeComponentClass( - name: string, - callback: ViewConfigGetter, - ): any, ReactNativeComponentTree: any, // TODO (bvaughn) Decide which additional types to expose here? // And how much information to fill in for the above types. diff --git a/packages/react-native-renderer/src/ReactNativeViewConfigRegistry.js b/packages/react-native-renderer/src/__mocks__/ReactNativeViewConfigRegistry.js similarity index 84% rename from packages/react-native-renderer/src/ReactNativeViewConfigRegistry.js rename to packages/react-native-renderer/src/__mocks__/ReactNativeViewConfigRegistry.js index 929bd8d4e52da..c8ed940a86095 100644 --- a/packages/react-native-renderer/src/ReactNativeViewConfigRegistry.js +++ b/packages/react-native-renderer/src/__mocks__/ReactNativeViewConfigRegistry.js @@ -6,18 +6,23 @@ * * @flow */ +'use strict'; import type { ReactNativeBaseComponentViewConfig, ViewConfigGetter, } from './ReactNativeTypes'; -import invariant from 'fbjs/lib/invariant'; +const invariant = require('fbjs/lib/invariant'); // Event configs -export const customBubblingEventTypes = {}; -export const customDirectEventTypes = {}; -export const eventTypes = {}; +const customBubblingEventTypes = {}; +const customDirectEventTypes = {}; +const eventTypes = {}; + +exports.customBubblingEventTypes = customBubblingEventTypes; +exports.customDirectEventTypes = customDirectEventTypes; +exports.eventTypes = eventTypes; const viewConfigCallbacks = new Map(); const viewConfigs = new Map(); @@ -64,7 +69,7 @@ function processEventTypes( * The callback is deferred until the view is actually rendered. * This is done to avoid causing Prepack deopts. */ -export function register(name: string, callback: ViewConfigGetter): string { +exports.register = function(name: string, callback: ViewConfigGetter): string { invariant( !viewConfigCallbacks.has(name), 'Tried to register two views with the same name %s', @@ -72,14 +77,14 @@ export function register(name: string, callback: ViewConfigGetter): string { ); viewConfigCallbacks.set(name, callback); return name; -} +}; /** * Retrieves a config for the specified view. * If this is the first time the view has been used, * This configuration will be lazy-loaded from UIManager. */ -export function get(name: string): ReactNativeBaseComponentViewConfig { +exports.get = function(name: string): ReactNativeBaseComponentViewConfig { let viewConfig; if (!viewConfigs.has(name)) { const callback = viewConfigCallbacks.get(name); @@ -97,4 +102,4 @@ export function get(name: string): ReactNativeBaseComponentViewConfig { } invariant(viewConfig, 'View config not found for name %s', name); return viewConfig; -} +}; diff --git a/packages/react-native-renderer/src/__mocks__/View.js b/packages/react-native-renderer/src/__mocks__/View.js deleted file mode 100644 index 5d5b57bd55134..0000000000000 --- a/packages/react-native-renderer/src/__mocks__/View.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict'; - -const createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; - -const View = createReactNativeComponentClass({ - validAttributes: {}, - uiViewClassName: 'View', -}); - -module.exports = View; diff --git a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js index f1e5b462b0e47..9d60af7ef8add 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js @@ -28,8 +28,8 @@ describe('ReactFabric', () => { ReactFabric = require('react-native-renderer/fabric'); FabricUIManager = require('FabricUIManager'); UIManager = require('UIManager'); - createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; + createReactNativeComponentClass = require('ReactNativeViewConfigRegistry') + .register; }); it('should be able to create and render a native component', () => { diff --git a/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js index 24c31f45eaa02..793f3bd3e70d6 100644 --- a/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactFabricAndNative-test.internal.js @@ -26,8 +26,8 @@ describe('ReactFabric', () => { React = require('react'); ReactFabric = require('react-native-renderer/fabric'); - createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; + createReactNativeComponentClass = require('ReactNativeViewConfigRegistry') + .register; }); it('find Fabric nodes with the RN renderer', () => { diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js index 4724db55f4621..d92c45c6fb915 100644 --- a/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactNativeError-test.internal.js @@ -25,8 +25,8 @@ describe('ReactNativeError', () => { React = require('react'); ReactNative = require('react-native-renderer'); - createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; + createReactNativeComponentClass = require('ReactNativeViewConfigRegistry') + .register; computeComponentStackForErrorReporting = ReactNative.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED .computeComponentStackForErrorReporting; diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js index e61f1c2d29d5a..07fbe1692aa6a 100644 --- a/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactNativeEvents-test.internal.js @@ -69,8 +69,8 @@ beforeEach(() => { ReactNative = require('react-native-renderer'); ResponderEventPlugin = require('events/ResponderEventPlugin').default; UIManager = require('UIManager'); - createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; + createReactNativeComponentClass = require('ReactNativeViewConfigRegistry') + .register; }); it('fails if unknown/unsupported event types are dispatched', () => { diff --git a/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js b/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js index c8d669e504f5b..f93a369d87251 100644 --- a/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js @@ -22,8 +22,8 @@ describe('ReactNative', () => { React = require('react'); ReactNative = require('react-native-renderer'); UIManager = require('UIManager'); - createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; + createReactNativeComponentClass = require('ReactNativeViewConfigRegistry') + .register; }); it('should be able to create and render a native component', () => { diff --git a/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js b/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js index 47f943fcd67c8..6ff7e8aeccbbe 100644 --- a/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js +++ b/packages/react-native-renderer/src/__tests__/createReactNativeComponentClass-test.internal.js @@ -18,8 +18,8 @@ describe('createReactNativeComponentClass', () => { beforeEach(() => { jest.resetModules(); - createReactNativeComponentClass = require('../createReactNativeComponentClass') - .default; + createReactNativeComponentClass = require('ReactNativeViewConfigRegistry') + .register; React = require('react'); ReactNative = require('react-native-renderer'); }); diff --git a/packages/react-native-renderer/src/createReactNativeComponentClass.js b/packages/react-native-renderer/src/createReactNativeComponentClass.js deleted file mode 100644 index d4308a2a715cc..0000000000000 --- a/packages/react-native-renderer/src/createReactNativeComponentClass.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import type {ViewConfigGetter} from './ReactNativeTypes'; - -import {register} from './ReactNativeViewConfigRegistry'; - -/** - * Creates a renderable ReactNative host component. - * Use this method for view configs that are loaded from UIManager. - * Use createReactNativeComponentClass() for view configs defined within JavaScript. - * - * @param {string} config iOS View configuration. - * @private - */ -const createReactNativeComponentClass = function( - name: string, - callback: ViewConfigGetter, -): string { - return register(name, callback); -}; - -export default createReactNativeComponentClass; diff --git a/scripts/flow/react-native-host-hooks.js b/scripts/flow/react-native-host-hooks.js index a6fe506c4ac34..de0bdbf012af8 100644 --- a/scripts/flow/react-native-host-hooks.js +++ b/scripts/flow/react-native-host-hooks.js @@ -9,6 +9,11 @@ /* eslint-disable */ +import type { + ReactNativeBaseComponentViewConfig, + ViewConfigGetter, +} from 'react-native-renderer/src/ReactNativeTypes'; + declare module 'deepDiffer' { declare module.exports: (one: any, two: any) => boolean; } @@ -142,11 +147,11 @@ declare module 'BatchedBridge' { declare function registerCallableModule(name: string, module: Object): void; } -declare module 'CSComponent' { - declare type Element = any; - declare type Options = any; -} +declare module 'ReactNativeViewConfigRegistry' { + declare var customBubblingEventTypes: Object; + declare var customDirectEventTypes: Object; + declare var eventTypes: Object; -declare module 'CSStatefulComponent' { - declare function CSStatefulComponent(spec: any): any; + declare function register(name: string, callback: ViewConfigGetter): string; + declare function get(name: string): ReactNativeBaseComponentViewConfig; } diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 2108c442d650d..138017b5d8a8a 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -124,10 +124,10 @@ const bundles = [ 'RCTEventEmitter', 'TextInputState', 'UIManager', - 'View', 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', + 'ReactNativeViewConfigRegistry', ], }, @@ -146,10 +146,10 @@ const bundles = [ 'TextInputState', 'UIManager', 'FabricUIManager', - 'View', 'deepDiffer', 'deepFreezeAndThrowOnMutationInDev', 'flattenStyle', + 'ReactNativeViewConfigRegistry', ], }, diff --git a/scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js b/scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js new file mode 100644 index 0000000000000..ffb0fa213f3b3 --- /dev/null +++ b/scripts/rollup/shims/react-native/ReactNativeViewConfigRegistry.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @providesModule ReactNativeViewConfigRegistry + * @flow + */ +'use strict'; + +import type { + ReactNativeBaseComponentViewConfig, + ViewConfigGetter, +} from './ReactNativeTypes'; + +const invariant = require('fbjs/lib/invariant'); + +// Event configs +const customBubblingEventTypes = {}; +const customDirectEventTypes = {}; +const eventTypes = {}; + +exports.customBubblingEventTypes = customBubblingEventTypes; +exports.customDirectEventTypes = customDirectEventTypes; +exports.eventTypes = eventTypes; + +const viewConfigCallbacks = new Map(); +const viewConfigs = new Map(); + +function processEventTypes( + viewConfig: ReactNativeBaseComponentViewConfig, +): void { + const {bubblingEventTypes, directEventTypes} = viewConfig; + + if (__DEV__) { + if (bubblingEventTypes != null && directEventTypes != null) { + for (const topLevelType in directEventTypes) { + invariant( + bubblingEventTypes[topLevelType] == null, + 'Event cannot be both direct and bubbling: %s', + topLevelType, + ); + } + } + } + + if (bubblingEventTypes != null) { + for (const topLevelType in bubblingEventTypes) { + if (customBubblingEventTypes[topLevelType] == null) { + eventTypes[topLevelType] = customBubblingEventTypes[topLevelType] = + bubblingEventTypes[topLevelType]; + } + } + } + + if (directEventTypes != null) { + for (const topLevelType in directEventTypes) { + if (customDirectEventTypes[topLevelType] == null) { + eventTypes[topLevelType] = customDirectEventTypes[topLevelType] = + directEventTypes[topLevelType]; + } + } + } +} + +/** + * Registers a native view/component by name. + * A callback is provided to load the view config from UIManager. + * The callback is deferred until the view is actually rendered. + * This is done to avoid causing Prepack deopts. + */ +exports.register = function(name: string, callback: ViewConfigGetter): string { + invariant( + !viewConfigCallbacks.has(name), + 'Tried to register two views with the same name %s', + name, + ); + viewConfigCallbacks.set(name, callback); + return name; +}; + +/** + * Retrieves a config for the specified view. + * If this is the first time the view has been used, + * This configuration will be lazy-loaded from UIManager. + */ +exports.get = function(name: string): ReactNativeBaseComponentViewConfig { + let viewConfig; + if (!viewConfigs.has(name)) { + const callback = viewConfigCallbacks.get(name); + invariant( + typeof callback === 'function', + 'View config not found for name %s', + name, + ); + viewConfigCallbacks.set(name, null); + viewConfig = callback(); + processEventTypes(viewConfig); + viewConfigs.set(name, viewConfig); + } else { + viewConfig = viewConfigs.get(name); + } + invariant(viewConfig, 'View config not found for name %s', name); + return viewConfig; +}; diff --git a/scripts/rollup/shims/react-native/createReactNativeComponentClass.js b/scripts/rollup/shims/react-native/createReactNativeComponentClass.js index d5d2b8c590093..c368255b6133e 100644 --- a/scripts/rollup/shims/react-native/createReactNativeComponentClass.js +++ b/scripts/rollup/shims/react-native/createReactNativeComponentClass.js @@ -10,9 +10,23 @@ 'use strict'; -const { - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, -} = require('ReactNative'); +import type {ViewConfigGetter} from './ReactNativeTypes'; -module.exports = - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.createReactNativeComponentClass; +const {register} = require('ReactNativeViewConfigRegistry'); + +/** + * Creates a renderable ReactNative host component. + * Use this method for view configs that are loaded from UIManager. + * Use createReactNativeComponentClass() for view configs defined within JavaScript. + * + * @param {string} config iOS View configuration. + * @private + */ +const createReactNativeComponentClass = function( + name: string, + callback: ViewConfigGetter, +): string { + return register(name, callback); +}; + +export default createReactNativeComponentClass;