update FabricUIManager to call the right JS object

Reviewed By: sebmarkbage

Differential Revision: D7037275

fbshipit-source-id: 6a1d13227910d0cdb99dde4b6c98ed7a20ef9911
This commit is contained in:
Kevin Gozali 2018-02-23 16:46:06 -08:00 committed by Facebook Github Bot
parent 25b0c374b3
commit 486ac9dc82
9 changed files with 408 additions and 35 deletions

View File

@ -0,0 +1,113 @@
/**
* 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 FabricView
* @flow
* @format
*/
'use strict';
/**
* This is a temporary fork of View.js for Fabric purpose.
* Do not use outside of Fabric tree.
*/
const Platform = require('Platform');
const React = require('React');
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const ViewPropTypes = require('ViewPropTypes');
const {NativeMethodsMixin} = require('ReactFabricInternals');
const {ViewContextTypes} = require('ViewContext');
const createReactClass = require('create-react-class');
const invariant = require('fbjs/lib/invariant');
const requireFabricComponent = require('requireFabricComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {ViewChildContext} from 'ViewContext';
export type Props = ViewProps;
/**
* The most fundamental component for building a UI.
*
* See http://facebook.github.io/react-native/docs/view.html
*/
const View = createReactClass({
displayName: 'View',
// TODO: We should probably expose the mixins, viewConfig, and statics publicly. For example,
// one of the props is of type AccessibilityComponentType. That is defined as a const[] above,
// but it is not rendered by the docs, since `statics` below is not rendered. So its Possible
// values had to be hardcoded.
mixins: [NativeMethodsMixin],
// `propTypes` should not be accessed directly on View since this wrapper only
// exists for DEV mode. However it's important for them to be declared.
// If the object passed to `createClass` specifies `propTypes`, Flow will
// create a static type from it.
propTypes: ViewPropTypes,
/**
* `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We
* make `this` look like an actual native component class.
*/
viewConfig: {
uiViewClassName: 'RCTView',
validAttributes: ReactNativeViewAttributes.RCTView,
},
childContextTypes: ViewContextTypes,
getChildContext(): ViewChildContext {
return {
isInAParentText: false,
};
},
render() {
invariant(
!(this.context.isInAParentText && Platform.OS === 'android'),
'Nesting of <View> within <Text> is not supported on Android.',
);
// WARNING: This method will not be used in production mode as in that mode we
// replace wrapper component View with generated native wrapper RCTView. Avoid
// adding functionality this component that you'd want to be available in both
// dev and prod modes.
return <RCTView {...this.props} />;
},
});
const RCTView = requireFabricComponent('RCTView', View, {
nativeOnly: {
nativeBackgroundAndroid: true,
nativeForegroundAndroid: true,
},
fabric: true,
});
if (__DEV__) {
const UIManager = require('UIManager');
const viewConfig =
(UIManager.viewConfigs && UIManager.viewConfigs.RCTView) || {};
for (const prop in viewConfig.nativeProps) {
const viewAny: any = View; // Appease flow
if (!viewAny.propTypes[prop] && !ReactNativeStyleAttributes[prop]) {
throw new Error(
'View is missing propType for native prop `' + prop + '`',
);
}
}
}
let ViewToExport = RCTView;
if (__DEV__) {
ViewToExport = View;
}
// No one should depend on the DEV-mode createClass View wrapper.
module.exports = ((ViewToExport: any): typeof RCTView);

View File

@ -27,6 +27,7 @@ type Props = {|
* suppresses an error when upgrading Flow's support for React. To see the
* error delete this comment and run Flow. */
children?: React.Children,
fabric?: boolean,
rootTag: number,
WrapperComponent?: ?React.ComponentType<*>,
|};
@ -90,7 +91,8 @@ class AppContainer extends React.Component<Props, State> {
render(): React.Node {
let yellowBox = null;
if (__DEV__) {
if (!global.__RCTProfileIsProfiling) {
if (!global.__RCTProfileIsProfiling && !this.props.fabric) {
// TODO: Fabric doesn't support YellowBox.
const YellowBox = require('YellowBox');
yellowBox = <YellowBox />;
}

View File

@ -10,53 +10,29 @@
*/
'use strict';
// TODO: fix the types
type Node = number;
type NodeSet = number;
// TODO: type these properly.
type Node = {};
type NodeSet = Array<Node>;
type NodeProps = {};
type InstanceHandle = {};
type Spec = {|
+createNode: (
reactTag: number,
viewName: string,
rootTag: number,
props: NodeProps,
instanceHandle: number,
instanceHandle: InstanceHandle,
) => Node,
+cloneNode: (node: Node) => Node,
+cloneNodeWithNewChildren: (node: Node) => Node,
+cloneNodeWithNewProps: (node: Node, newProps: NodeProps) => Node,
+cloneNodeWithNewChildrenAndProps: (node: Node, newProps: NodeProps) => Node,
+createChildSet: (rootTag: number) => NodeSet,
+appendChild: (parentNode: Node, child: Node) => Node,
+appendChildToSet: (childSet: NodeSet, child: Node) => void,
+completeRoot: (rootTag: number, childSet: NodeSet) => void,
|};
const NativeFabricUIManager: Spec = require('NativeModules').FabricUIManager;
const FabricUIManager: Spec = {
createNode(
reactTag: number,
viewName: string,
rootTag: number,
props: NodeProps,
instanceHandle: number,
): number {
return NativeFabricUIManager.createNode(
reactTag,
viewName,
rootTag,
props,
0, // TODO: instanceHandle is cannot be JSON serialized.
);
},
cloneNode: NativeFabricUIManager.cloneNode,
cloneNodeWithNewChildren: NativeFabricUIManager.cloneNodeWithNewChildren,
cloneNodeWithNewProps: NativeFabricUIManager.cloneNodeWithNewProps,
cloneNodeWithNewChildrenAndProps:
NativeFabricUIManager.cloneNodeWithNewChildrenAndProps,
appendChild: NativeFabricUIManager.appendChild,
appendChildToSet: NativeFabricUIManager.appendChildToSet,
completeRoot: NativeFabricUIManager.completeRoot,
};
const FabricUIManager: ?Spec = global.nativeFabricUIManager;
module.exports = FabricUIManager;

View File

@ -30,7 +30,10 @@ function renderFabricSurface<Props: Object>(
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
let renderable = (
<AppContainer rootTag={rootTag} WrapperComponent={WrapperComponent}>
<AppContainer
fabric={true}
rootTag={rootTag}
WrapperComponent={WrapperComponent}>
<RootComponent {...initialProps} rootTag={rootTag} />
</AppContainer>
);

View File

@ -0,0 +1,240 @@
/**
* 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 requireFabricComponent
* @flow
* @format
*/
'use strict';
const Platform = require('Platform');
const {
ReactNativeBridgeEventPlugin,
createReactNativeComponentClass,
} = require('ReactFabricInternals');
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
const UIManager = require('UIManager');
const insetsDiffer = require('insetsDiffer');
const matricesDiffer = require('matricesDiffer');
const pointsDiffer = require('pointsDiffer');
const processColor = require('processColor');
const resolveAssetSource = require('resolveAssetSource');
const sizesDiffer = require('sizesDiffer');
const verifyPropTypes = require('verifyPropTypes');
const invariant = require('fbjs/lib/invariant');
const warning = require('fbjs/lib/warning');
/**
* Used to create React components that directly wrap native component
* implementations. Config information is extracted from data exported from the
* UIManager module. You should also wrap the native component in a
* hand-written component with full propTypes definitions and other
* documentation - pass the hand-written component in as `componentInterface` to
* verify all the native props are documented via `propTypes`.
*
* If some native props shouldn't be exposed in the wrapper interface, you can
* pass null for `componentInterface` and call `verifyPropTypes` directly
* with `nativePropsToIgnore`;
*
* Common types are lined up with the appropriate prop differs with
* `TypeToDifferMap`. Non-scalar types not in the map default to `deepDiffer`.
*/
import type {ComponentInterface} from 'verifyPropTypes';
let hasAttachedDefaultEventTypes: boolean = false;
function requireNativeComponent(
viewName: string,
componentInterface?: ?ComponentInterface,
extraConfig?: ?{nativeOnly?: Object},
): React$ComponentType<any> | string {
function attachDefaultEventTypes(viewConfig: any) {
if (Platform.OS === 'android') {
// This is supported on Android platform only,
// as lazy view managers discovery is Android-specific.
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];
invariant(
viewConfig != null && !viewConfig.NativeProps != null,
'Native component for "%s" does not exist',
viewName,
);
viewConfig.uiViewClassName = viewName;
viewConfig.validAttributes = {};
// ReactNative `View.propTypes` have been deprecated in favor of
// `ViewPropTypes`. In their place a temporary getter has been added with a
// deprecated warning message. Avoid triggering that warning here by using
// temporary workaround, __propTypesSecretDontUseThesePlease.
// TODO (bvaughn) Revert this particular change any time after April 1
if (componentInterface) {
viewConfig.propTypes =
typeof componentInterface.__propTypesSecretDontUseThesePlease ===
'object'
? componentInterface.__propTypesSecretDontUseThesePlease
: componentInterface.propTypes;
} else {
viewConfig.propTypes = null;
}
let baseModuleName = viewConfig.baseModuleName;
let bubblingEventTypes = viewConfig.bubblingEventTypes;
let directEventTypes = viewConfig.directEventTypes;
let nativeProps = viewConfig.NativeProps;
while (baseModuleName) {
const baseModule = UIManager[baseModuleName];
if (!baseModule) {
warning(false, 'Base module "%s" does not exist', baseModuleName);
baseModuleName = null;
} else {
bubblingEventTypes = {
...baseModule.bubblingEventTypes,
...bubblingEventTypes,
};
directEventTypes = {
...baseModule.directEventTypes,
...directEventTypes,
};
nativeProps = {
...baseModule.NativeProps,
...nativeProps,
};
baseModuleName = baseModule.baseModuleName;
}
}
viewConfig.bubblingEventTypes = bubblingEventTypes;
viewConfig.directEventTypes = directEventTypes;
for (const key in nativeProps) {
let useAttribute = false;
const attribute = {};
const differ = TypeToDifferMap[nativeProps[key]];
if (differ) {
attribute.diff = differ;
useAttribute = true;
}
const processor = TypeToProcessorMap[nativeProps[key]];
if (processor) {
attribute.process = processor;
useAttribute = true;
}
viewConfig.validAttributes[key] = useAttribute ? attribute : true;
}
// Unfortunately, the current set up puts the style properties on the top
// level props object. We also need to add the nested form for API
// compatibility. This allows these props on both the top level and the
// nested style level. TODO: Move these to nested declarations on the
// native side.
viewConfig.validAttributes.style = ReactNativeStyleAttributes;
if (__DEV__) {
componentInterface &&
verifyPropTypes(
componentInterface,
viewConfig,
extraConfig && extraConfig.nativeOnly,
);
}
if (!hasAttachedDefaultEventTypes) {
attachDefaultEventTypes(viewConfig);
hasAttachedDefaultEventTypes = true;
}
// Register this view's event types with the ReactNative renderer.
// This enables view managers to be initialized lazily, improving perf,
// While also enabling 3rd party components to define custom event types.
ReactNativeBridgeEventPlugin.processEventTypes(viewConfig);
return viewConfig;
}
return createReactNativeComponentClass(viewName, getViewConfig);
}
const TypeToDifferMap = {
// iOS Types
CATransform3D: matricesDiffer,
CGPoint: pointsDiffer,
CGSize: sizesDiffer,
UIEdgeInsets: insetsDiffer,
// Android Types
// (not yet implemented)
};
function processColorArray(colors: ?Array<any>): ?Array<?number> {
return colors && colors.map(processColor);
}
const TypeToProcessorMap = {
// iOS Types
CGColor: processColor,
CGColorArray: processColorArray,
UIColor: processColor,
UIColorArray: processColorArray,
CGImage: resolveAssetSource,
UIImage: resolveAssetSource,
RCTImageSource: resolveAssetSource,
// Android Types
Color: processColor,
ColorArray: processColorArray,
};
module.exports = requireNativeComponent;

View File

@ -2760,7 +2760,8 @@ var ReactNativeGlobalResponderHandler = {
/**
* Register the event emitter with the native bridge
*/
RCTEventEmitter.register(ReactNativeEventEmitter);
// TODO: This event emitter is interfering with the existing ReactNative renderer.
// RCTEventEmitter.register(ReactNativeEventEmitter);
/**
* Inject module for resolving DOM hierarchy and plugin ordering.

View File

@ -1140,7 +1140,8 @@ var ReactNativeEventEmitter = Object.freeze({
}
}
});
RCTEventEmitter.register(ReactNativeEventEmitter);
// TODO: This event emitter is interfering with the existing ReactNative renderer.
// RCTEventEmitter.register(ReactNativeEventEmitter);
injection.injectEventPluginOrder([
"ResponderEventPlugin",
"ReactNativeBridgeEventPlugin"

View File

@ -0,0 +1,32 @@
/**
* 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 ReactFabricInternals
* @flow
* @format
*/
'use strict';
const {
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
} = require('ReactFabric');
import type {NativeMethodsMixinType} from 'ReactNativeTypes';
const {
NativeMethodsMixin,
ReactNativeBridgeEventPlugin,
createReactNativeComponentClass,
} = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
module.exports = {
NativeMethodsMixin: ((NativeMethodsMixin: any): $Exact<
NativeMethodsMixinType,
>),
ReactNativeBridgeEventPlugin,
createReactNativeComponentClass,
};

View File

@ -60,6 +60,11 @@
- (void)appendChildToSet:(NSMutableArray<RCTShadowView *> *)childSet
childNode:(RCTShadowView *)childNode
{
if (!childNode) {
// TODO: until this class is fully implemented, we may get nil from JS.
return;
}
[childSet addObject:childNode];
}