RN: Simpify `requireNativeComponent`
Reviewed By: sahrens Differential Revision: D7893592 fbshipit-source-id: bfe7772ff2fa785fc7c5ad1f7dc0dbe97b6ffb11
This commit is contained in:
parent
28d37781c6
commit
b549e364e0
|
@ -7,6 +7,7 @@
|
||||||
* @flow
|
* @flow
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const Platform = require('Platform');
|
const Platform = require('Platform');
|
||||||
|
@ -24,6 +25,14 @@ const verifyPropTypes = require('verifyPropTypes');
|
||||||
const invariant = require('fbjs/lib/invariant');
|
const invariant = require('fbjs/lib/invariant');
|
||||||
const warning = require('fbjs/lib/warning');
|
const warning = require('fbjs/lib/warning');
|
||||||
|
|
||||||
|
import type {ComponentInterface} from 'verifyPropTypes';
|
||||||
|
|
||||||
|
type ExtraOptions = $ReadOnly<{|
|
||||||
|
nativeOnly?: $ReadOnly<{
|
||||||
|
[propName: string]: boolean,
|
||||||
|
}>,
|
||||||
|
|}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to create React components that directly wrap native component
|
* Used to create React components that directly wrap native component
|
||||||
* implementations. Config information is extracted from data exported from the
|
* implementations. Config information is extracted from data exported from the
|
||||||
|
@ -39,81 +48,23 @@ const warning = require('fbjs/lib/warning');
|
||||||
* Common types are lined up with the appropriate prop differs with
|
* Common types are lined up with the appropriate prop differs with
|
||||||
* `TypeToDifferMap`. Non-scalar types not in the map default to `deepDiffer`.
|
* `TypeToDifferMap`. Non-scalar types not in the map default to `deepDiffer`.
|
||||||
*/
|
*/
|
||||||
import type {ComponentInterface} from 'verifyPropTypes';
|
const requireNativeComponent = (
|
||||||
|
|
||||||
let hasAttachedDefaultEventTypes: boolean = false;
|
|
||||||
|
|
||||||
function requireNativeComponent(
|
|
||||||
viewName: string,
|
viewName: string,
|
||||||
componentInterface?: ?ComponentInterface,
|
componentInterface?: ?ComponentInterface,
|
||||||
extraConfig?: ?{nativeOnly?: Object},
|
extraConfig?: ?ExtraOptions,
|
||||||
): React$ComponentType<any> | string {
|
): string =>
|
||||||
function attachDefaultEventTypes(viewConfig: any) {
|
createReactNativeComponentClass(viewName, () => {
|
||||||
// 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.
|
|
||||||
if (UIManager.ViewManagerNames) {
|
|
||||||
// Lazy view managers enabled.
|
|
||||||
viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes());
|
|
||||||
} else {
|
|
||||||
viewConfig.bubblingEventTypes = merge(
|
|
||||||
viewConfig.bubblingEventTypes,
|
|
||||||
UIManager.genericBubblingEventTypes,
|
|
||||||
);
|
|
||||||
viewConfig.directEventTypes = merge(
|
|
||||||
viewConfig.directEventTypes,
|
|
||||||
UIManager.genericDirectEventTypes,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't load the ViewConfig from UIManager until it's needed for rendering.
|
|
||||||
// Lazy-loading this can help avoid Prepack deopts.
|
|
||||||
function getViewConfig() {
|
|
||||||
const viewConfig = UIManager[viewName];
|
const viewConfig = UIManager[viewName];
|
||||||
|
|
||||||
invariant(
|
invariant(
|
||||||
viewConfig != null && viewConfig.NativeProps != null,
|
viewConfig != null && viewConfig.NativeProps != null,
|
||||||
'Native component for "%s" does not exist',
|
'requireNativeComponent: "%s" was not found in the UIManager.',
|
||||||
viewName,
|
viewName,
|
||||||
);
|
);
|
||||||
|
|
||||||
viewConfig.uiViewClassName = viewName;
|
// TODO: This seems like a whole lot of runtime initialization for every
|
||||||
viewConfig.validAttributes = {};
|
// native component that can be either avoided or simplified.
|
||||||
viewConfig.propTypes =
|
let {baseModuleName, bubblingEventTypes, directEventTypes} = viewConfig;
|
||||||
componentInterface == null ? null : componentInterface.propTypes;
|
|
||||||
|
|
||||||
let baseModuleName = viewConfig.baseModuleName;
|
|
||||||
let bubblingEventTypes = viewConfig.bubblingEventTypes;
|
|
||||||
let directEventTypes = viewConfig.directEventTypes;
|
|
||||||
let nativeProps = viewConfig.NativeProps;
|
let nativeProps = viewConfig.NativeProps;
|
||||||
while (baseModuleName) {
|
while (baseModuleName) {
|
||||||
const baseModule = UIManager[baseModuleName];
|
const baseModule = UIManager[baseModuleName];
|
||||||
|
@ -137,33 +88,41 @@ function requireNativeComponent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewConfig.bubblingEventTypes = bubblingEventTypes;
|
const viewAttributes = {};
|
||||||
viewConfig.directEventTypes = directEventTypes;
|
|
||||||
|
|
||||||
for (const key in nativeProps) {
|
for (const key in nativeProps) {
|
||||||
const typeName = nativeProps[key];
|
const typeName = nativeProps[key];
|
||||||
const diff = getDifferForType(typeName);
|
const diff = getDifferForType(typeName);
|
||||||
const process = getProcessorForType(typeName);
|
const process = getProcessorForType(typeName);
|
||||||
|
|
||||||
viewConfig.validAttributes[key] =
|
viewAttributes[key] =
|
||||||
diff == null && process == null ? true : {diff, process};
|
diff == null && process == null ? true : {diff, process};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, the current set up puts the style properties on the top
|
// Unfortunately, the current setup declares style properties as top-level
|
||||||
// level props object. We also need to add the nested form for API
|
// props. This makes it so we allow style properties in the `style` prop.
|
||||||
// compatibility. This allows these props on both the top level and the
|
// TODO: Move style properties into a `style` prop and disallow them as
|
||||||
// nested style level. TODO: Move these to nested declarations on the
|
// top-level props on the native side.
|
||||||
// native side.
|
viewAttributes.style = ReactNativeStyleAttributes;
|
||||||
viewConfig.validAttributes.style = ReactNativeStyleAttributes;
|
|
||||||
|
Object.assign(viewConfig, {
|
||||||
|
uiViewClassName: viewName,
|
||||||
|
validAttributes: viewAttributes,
|
||||||
|
propTypes:
|
||||||
|
componentInterface == null ? null : componentInterface.propTypes,
|
||||||
|
bubblingEventTypes,
|
||||||
|
directEventTypes,
|
||||||
|
});
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
componentInterface &&
|
if (componentInterface != null) {
|
||||||
verifyPropTypes(
|
verifyPropTypes(
|
||||||
componentInterface,
|
componentInterface,
|
||||||
viewConfig,
|
viewConfig,
|
||||||
extraConfig && extraConfig.nativeOnly,
|
extraConfig == null ? null : extraConfig.nativeOnly,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasAttachedDefaultEventTypes) {
|
if (!hasAttachedDefaultEventTypes) {
|
||||||
attachDefaultEventTypes(viewConfig);
|
attachDefaultEventTypes(viewConfig);
|
||||||
|
@ -171,9 +130,57 @@ function requireNativeComponent(
|
||||||
}
|
}
|
||||||
|
|
||||||
return viewConfig;
|
return viewConfig;
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Figure out how this makes sense. We're using a global boolean to only
|
||||||
|
// initialize this on the first eagerly initialized native component.
|
||||||
|
let hasAttachedDefaultEventTypes = false;
|
||||||
|
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.
|
||||||
|
if (UIManager.ViewManagerNames) {
|
||||||
|
// Lazy view managers enabled.
|
||||||
|
viewConfig = merge(viewConfig, UIManager.getDefaultEventTypes());
|
||||||
|
} else {
|
||||||
|
viewConfig.bubblingEventTypes = merge(
|
||||||
|
viewConfig.bubblingEventTypes,
|
||||||
|
UIManager.genericBubblingEventTypes,
|
||||||
|
);
|
||||||
|
viewConfig.directEventTypes = merge(
|
||||||
|
viewConfig.directEventTypes,
|
||||||
|
UIManager.genericDirectEventTypes,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return createReactNativeComponentClass(viewName, getViewConfig);
|
// 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(
|
function getDifferForType(
|
||||||
|
|
Loading…
Reference in New Issue