[ReactNative] Rename ReactIOS JS module (and relatives) to ReactNative.

This commit is contained in:
Krzysztof Magiera
2015-05-08 09:45:43 -07:00
committed by Christopher Chedeau
parent 1db2f07192
commit ff00e1496c
37 changed files with 239 additions and 239 deletions

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule React
* @flow
*/
"use strict";
module.exports = require('ReactNative');

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNative
* @flow
*/
"use strict";
var ReactChildren = require('ReactChildren');
var ReactClass = require('ReactClass');
var ReactComponent = require('ReactComponent');
var ReactContext = require('ReactContext');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactElement = require('ReactElement');
var ReactElementValidator = require('ReactElementValidator');
var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactNativeDefaultInjection = require('ReactNativeDefaultInjection');
var ReactNativeMount = require('ReactNativeMount');
var ReactPropTypes = require('ReactPropTypes');
var deprecated = require('deprecated');
var invariant = require('invariant');
var onlyChild = require('onlyChild');
ReactNativeDefaultInjection.inject();
var createElement = ReactElement.createElement;
var createFactory = ReactElement.createFactory;
var cloneElement = ReactElement.cloneElement;
if (__DEV__) {
createElement = ReactElementValidator.createElement;
createFactory = ReactElementValidator.createFactory;
cloneElement = ReactElementValidator.cloneElement;
}
var resolveDefaultProps = function(element) {
// Could be optimized, but not currently in heavy use.
var defaultProps = element.type.defaultProps;
var props = element.props;
for (var propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
};
// Experimental optimized element creation
var augmentElement = function(element: ReactElement) {
if (__DEV__) {
invariant(
false,
'This optimized path should never be used in DEV mode because ' +
'it does not provide validation. Check your JSX transform.'
);
}
element._owner = ReactCurrentOwner.current;
element._context = ReactContext.current;
if (element.type.defaultProps) {
resolveDefaultProps(element);
}
return element;
};
var render = function(
element: ReactElement,
mountInto: number,
callback?: ?(() => void)
): ?ReactComponent {
return ReactNativeMount.renderComponent(element, mountInto, callback);
};
var ReactNative = {
hasReactNativeInitialized: false,
Children: {
map: ReactChildren.map,
forEach: ReactChildren.forEach,
count: ReactChildren.count,
only: onlyChild
},
Component: ReactComponent,
PropTypes: ReactPropTypes,
createClass: ReactClass.createClass,
createElement: createElement,
createFactory: createFactory,
cloneElement: cloneElement,
_augmentElement: augmentElement,
render: render,
unmountComponentAtNode: ReactNativeMount.unmountComponentAtNode,
// Hook for JSX spread, don't use this for anything else.
__spread: Object.assign,
unmountComponentAtNodeAndRemoveContainer: ReactNativeMount.unmountComponentAtNodeAndRemoveContainer,
isValidClass: ReactElement.isValidFactory,
isValidElement: ReactElement.isValidElement,
// Deprecations (remove for 0.13)
renderComponent: deprecated(
'React',
'renderComponent',
'render',
this,
render
),
isValidComponent: deprecated(
'React',
'isValidComponent',
'isValidElement',
this,
ReactElement.isValidElement
)
};
// Inject the runtime into a devtools global hook regardless of browser.
// Allows for debugging when the hook is injected on the page.
/* globals __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
CurrentOwner: ReactCurrentOwner,
InstanceHandles: ReactInstanceHandles,
Mount: ReactNativeMount,
Reconciler: require('ReactReconciler'),
TextComponent: require('ReactNativeTextComponent'),
});
}
module.exports = ReactNative;

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeComponentEnvironment
* @flow
*/
'use strict';
var ReactNativeDOMIDOperations = require('ReactNativeDOMIDOperations');
var ReactNativeReconcileTransaction = require('ReactNativeReconcileTransaction');
var ReactNativeComponentEnvironment = {
processChildrenUpdates: ReactNativeDOMIDOperations.dangerouslyProcessChildrenUpdates,
replaceNodeWithMarkupByID: ReactNativeDOMIDOperations.dangerouslyReplaceNodeWithMarkupByID,
/**
* Nothing to do for UIKit bridge.
*
* @private
*/
unmountIDFromEnvironment: function(/*rootNodeID*/) {
},
/**
* @param {DOMElement} Element to clear.
*/
clearNode: function(/*containerView*/) {
},
ReactReconcileTransaction: ReactNativeReconcileTransaction,
};
module.exports = ReactNativeComponentEnvironment;

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeComponentMixin
* @flow
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactInstanceMap = require('ReactInstanceMap');
/**
* ReactNative vs ReactWeb
* -----------------------
* React treats some pieces of data opaquely. This means that the information
* is first class (it can be passed around), but cannot be inspected. This
* allows us to build infrastructure that reasons about resources, without
* making assumptions about the nature of those resources, and this allows that
* infra to be shared across multiple platforms, where the resources are very
* different. General infra (such as `ReactMultiChild`) reasons opaquely about
* the data, but platform specific code (such as `ReactNativeBaseComponent`) can
* make assumptions about the data.
*
*
* `rootNodeID`, uniquely identifies a position in the generated native view
* tree. Many layers of composite components (created with `React.createClass`)
* can all share the same `rootNodeID`.
*
* `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
* resource (dom node, native view etc). The `rootNodeID` is sufficient for web
* `nodeHandle`s, because the position in a tree is always enough to uniquely
* identify a DOM node (we never have nodes in some bank outside of the
* document). The same would be true for `ReactNative`, but we must maintain a
* mapping that we can send efficiently serializable
* strings across native boundaries.
*
* Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
* ----------------------------------------------------------------------------
* nodeHandle N/A rootNodeID tag
*
*
* `mountImage`: A way to represent the potential to create lower level
* resources whos `nodeHandle` can be discovered immediately by knowing the
* `rootNodeID`. Today's web React represents this with `innerHTML` annotated
* with DOM ids that match the `rootNodeID`.
*
* Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
* ----------------------------------------------------------------------------
* mountImage innerHTML innerHTML {rootNodeID, tag}
*
*/
var ReactNativeComponentMixin = {
/**
* This has no particular meaning in ReactNative. If this were in the DOM, this
* would return the DOM node. There should be nothing that invokes this
* method. Any current callers of this are mistaken - they should be invoking
* `getNodeHandle`.
*/
getNativeNode: function() {
// TODO (balpert): Wrap iOS native components in a composite wrapper, then
// ReactInstanceMap.get here will always succeed
return ReactNativeTagHandles.rootNodeIDToTag[
(ReactInstanceMap.get(this) || this)._rootNodeID
];
},
getNodeHandle: function() {
return ReactNativeTagHandles.rootNodeIDToTag[
(ReactInstanceMap.get(this) || this)._rootNodeID
];
}
};
module.exports = ReactNativeComponentMixin;

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeDOMIDOperations
* @flow
*/
"use strict";
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var RCTUIManager = require('NativeModules').UIManager;
var ReactPerf = require('ReactPerf');
/**
* Updates a component's children by processing a series of updates.
* For each of the update/create commands, the `fromIndex` refers to the index
* that the item existed at *before* any of the updates are applied, and the
* `toIndex` refers to the index after *all* of the updates are applied
* (including deletes/moves). TODO: refactor so this can be shared with
* DOMChildrenOperations.
*
* @param {array<object>} updates List of update configurations.
* @param {array<string>} markup List of markup strings - in the case of React
* IOS, the ids of new components assumed to be already created.
*/
var dangerouslyProcessChildrenUpdates = function(childrenUpdates, markupList) {
if (!childrenUpdates.length) {
return;
}
var byContainerTag = {};
// Group by parent ID - send them across the bridge in separate commands per
// containerID.
for (var i = 0; i < childrenUpdates.length; i++) {
var update = childrenUpdates[i];
var containerTag = ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(update.parentID);
var updates = byContainerTag[containerTag] || (byContainerTag[containerTag] = {});
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING) {
(updates.moveFromIndices || (updates.moveFromIndices = [])).push(update.fromIndex);
(updates.moveToIndices || (updates.moveToIndices = [])).push(update.toIndex);
} else if (update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
(updates.removeAtIndices || (updates.removeAtIndices = [])).push(update.fromIndex);
} else if (update.type === ReactMultiChildUpdateTypes.INSERT_MARKUP) {
var mountImage = markupList[update.markupIndex];
var tag = mountImage.tag;
var rootNodeID = mountImage.rootNodeID;
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(rootNodeID, tag);
(updates.addAtIndices || (updates.addAtIndices = [])).push(update.toIndex);
(updates.addChildTags || (updates.addChildTags = [])).push(tag);
}
}
// Note this enumeration order will be different on V8! Move `byContainerTag`
// to a sparse array as soon as we confirm there are not horrible perf
// penalties.
for (var updateParentTagString in byContainerTag) {
var updateParentTagNumber = +updateParentTagString;
var childUpdatesToSend = byContainerTag[updateParentTagNumber];
RCTUIManager.manageChildren(
updateParentTagNumber,
childUpdatesToSend.moveFromIndices,
childUpdatesToSend.moveToIndices,
childUpdatesToSend.addChildTags,
childUpdatesToSend.addAtIndices,
childUpdatesToSend.removeAtIndices
);
}
};
/**
* Operations used to process updates to DOM nodes. This is made injectable via
* `ReactComponent.DOMIDOperations`.
*/
var ReactNativeDOMIDOperations = {
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactDOMIDOperations',
'dangerouslyProcessChildrenUpdates',
dangerouslyProcessChildrenUpdates
),
/**
* Replaces a view that exists in the document with markup.
*
* @param {string} id ID of child to be replaced.
* @param {string} markup Mount image to replace child with id.
*/
dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure(
'ReactDOMIDOperations',
'dangerouslyReplaceNodeWithMarkupByID',
function(id, mountImage) {
var oldTag = ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(id);
RCTUIManager.replaceExistingNonRootView(oldTag, mountImage.tag);
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(id, mountImage.tag);
}
),
};
module.exports = ReactNativeDOMIDOperations;

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeDefaultInjection
* @flow
*/
"use strict";
/**
* Make sure `setTimeout`/`setInterval` are patched correctly.
*/
require('InitializeJavaScriptAppEngine');
var EventPluginHub = require('EventPluginHub');
var EventPluginUtils = require('EventPluginUtils');
var IOSDefaultEventPluginOrder = require('IOSDefaultEventPluginOrder');
var IOSNativeBridgeEventPlugin = require('IOSNativeBridgeEventPlugin');
var NodeHandle = require('NodeHandle');
var ReactClass = require('ReactClass');
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactNativeComponentEnvironment = require('ReactNativeComponentEnvironment');
var ReactNativeComponentMixin = require('ReactNativeComponentMixin');
var ReactNativeGlobalInteractionHandler = require('ReactNativeGlobalInteractionHandler');
var ReactNativeGlobalResponderHandler = require('ReactNativeGlobalResponderHandler');
var ReactNativeMount = require('ReactNativeMount');
var ReactNativeTextComponent = require('ReactNativeTextComponent');
var ReactNativeComponent = require('ReactNativeComponent');
var ReactUpdates = require('ReactUpdates');
var ResponderEventPlugin = require('ResponderEventPlugin');
var UniversalWorkerNodeHandle = require('UniversalWorkerNodeHandle');
var createReactNativeComponentClass = require('createReactNativeComponentClass');
var invariant = require('invariant');
// Just to ensure this gets packaged, since its only caller is from Native.
require('RCTEventEmitter');
require('RCTLog');
require('RCTJSTimers');
function inject() {
/**
* Inject module for resolving DOM hierarchy and plugin ordering.
*/
EventPluginHub.injection.injectEventPluginOrder(IOSDefaultEventPluginOrder);
EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
ResponderEventPlugin.injection.injectGlobalResponderHandler(
ReactNativeGlobalResponderHandler
);
ResponderEventPlugin.injection.injectGlobalInteractionHandler(
ReactNativeGlobalInteractionHandler
);
/**
* Some important event plugins included by default (without having to require
* them).
*/
EventPluginHub.injection.injectEventPluginsByName({
'ResponderEventPlugin': ResponderEventPlugin,
'IOSNativeBridgeEventPlugin': IOSNativeBridgeEventPlugin
});
ReactUpdates.injection.injectReconcileTransaction(
ReactNativeComponentEnvironment.ReactReconcileTransaction
);
ReactUpdates.injection.injectBatchingStrategy(
ReactDefaultBatchingStrategy
);
ReactComponentEnvironment.injection.injectEnvironment(
ReactNativeComponentEnvironment
);
// Can't import View here because it depends on React to make its composite
var RCTView = createReactNativeComponentClass({
validAttributes: {},
uiViewClassName: 'RCTView',
});
ReactEmptyComponent.injection.injectEmptyComponent(RCTView);
EventPluginUtils.injection.injectMount(ReactNativeMount);
ReactClass.injection.injectMixin(ReactNativeComponentMixin);
ReactNativeComponent.injection.injectTextComponentClass(
ReactNativeTextComponent
);
ReactNativeComponent.injection.injectAutoWrapper(function(tag) {
// Show a nicer error message for non-function tags
var info = '';
if (typeof tag === 'string' && /^[a-z]/.test(tag)) {
info += ' Each component name should start with an uppercase letter.';
}
invariant(false, 'Expected a component class, got %s.%s', tag, info);
});
NodeHandle.injection.injectImplementation(UniversalWorkerNodeHandle);
}
module.exports = {
inject: inject,
};

View File

@@ -0,0 +1,214 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeEventEmitter
* @flow
*/
"use strict";
var EventPluginHub = require('EventPluginHub');
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var NodeHandle = require('NodeHandle');
var EventConstants = require('EventConstants');
var merge = require('merge');
var warning = require('warning');
var topLevelTypes = EventConstants.topLevelTypes;
/**
* Version of `ReactBrowserEventEmitter` that works on the receiving side of a
* serialized worker boundary.
*/
// Shared default empty native event - conserve memory.
var EMPTY_NATIVE_EVENT = {};
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices by which to pull subsequence.
* @return {Array<Touch>} Subsequence of touch objects.
*/
var touchSubsequence = function(touches, indices) {
var ret = [];
for (var i = 0; i < indices.length; i++) {
ret.push(touches[indices[i]]);
}
return ret;
};
/**
* TODO: Pool all of this.
*
* Destroys `touches` by removing touch objects at indices `indices`. This is
* to maintain compatibility with W3C touch "end" events, where the active
* touches don't include the set that has just been "ended".
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
var removeTouchesAtIndices = function(
touches: Array<Object>,
indices: Array<number>
): Array<Object> {
var rippedOut = [];
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
var temp: Array<?Object> = (touches: Array<any>);
for (var i = 0; i < indices.length; i++) {
var index = indices[i];
rippedOut.push(touches[index]);
temp[index] = null;
}
var fillAt = 0;
for (var j = 0; j < temp.length; j++) {
var cur = temp[j];
if (cur !== null) {
temp[fillAt++] = cur;
}
}
temp.length = fillAt;
return rippedOut;
};
/**
* `ReactNativeEventEmitter` is used to attach top-level event listeners. For example:
*
* ReactNativeEventEmitter.putListener('myID', 'onClick', myFunction);
*
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
*
* @internal
*/
var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
registrationNames: EventPluginHub.registrationNameModules,
putListener: EventPluginHub.putListener,
getListener: EventPluginHub.getListener,
deleteListener: EventPluginHub.deleteListener,
deleteAllListeners: EventPluginHub.deleteAllListeners,
/**
* Internal version of `receiveEvent` in terms of normalized (non-tag)
* `rootNodeID`.
*
* @see receiveEvent.
*
* @param {rootNodeID} rootNodeID React root node ID that event occured on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
_receiveRootNodeIDEvent: function(
rootNodeID: ?string,
topLevelType: string,
nativeEventParam: Object
) {
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
ReactNativeEventEmitter.handleTopLevel(
topLevelType,
rootNodeID,
rootNodeID,
nativeEvent
);
},
/**
* Publically exposed method on module for native objc to invoke when a top
* level event is extracted.
* @param {rootNodeID} rootNodeID React root node ID that event occured on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
receiveEvent: function(
tag: number,
topLevelType: string,
nativeEventParam: Object
) {
var rootNodeID = ReactNativeTagHandles.tagToRootNodeID[tag];
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
topLevelType,
nativeEventParam
);
},
/**
* Simple multi-wrapper around `receiveEvent` that is intended to receive an
* efficient representation of `Touch` objects, and other information that
* can be used to construct W3C compliant `Event` and `Touch` lists.
*
* This may create dispatch behavior that differs than web touch handling. We
* loop through each of the changed touches and receive it as a single event.
* So two `touchStart`/`touchMove`s that occur simultaneously are received as
* two separate touch event dispatches - when they arguably should be one.
*
* This implementation reuses the `Touch` objects themselves as the `Event`s
* since we dispatch an event for each touch (though that might not be spec
* compliant). The main purpose of reusing them is to save allocations.
*
* TODO: Dispatch multiple changed touches in one event. The bubble path
* could be the first common ancestor of all the `changedTouches`.
*
* One difference between this behavior and W3C spec: cancelled touches will
* not appear in `.touches`, or in any future `.touches`, though they may
* still be "actively touching the surface".
*
* Web desktop polyfills only need to construct a fake touch event with
* identifier 0, also abandoning traditional click handlers.
*/
receiveTouches: function(
eventTopLevelType: string,
touches: Array<Object>,
changedIndices: Array<number>
) {
var changedTouches =
eventTopLevelType === topLevelTypes.topTouchEnd ||
eventTopLevelType === topLevelTypes.topTouchCancel ?
removeTouchesAtIndices(touches, changedIndices) :
touchSubsequence(touches, changedIndices);
for (var jj = 0; jj < changedTouches.length; jj++) {
var touch = changedTouches[jj];
// Touch objects can fullfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.changedTouches = changedTouches;
touch.touches = touches;
var nativeEvent = touch;
var rootNodeID = null;
var target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < ReactNativeTagHandles.tagsStartAt) {
if (__DEV__) {
warning(
false,
'A view is reporting that a touch occured on tag zero.'
);
}
} else {
rootNodeID = NodeHandle.getRootNodeID(target);
}
}
ReactNativeEventEmitter._receiveRootNodeIDEvent(
rootNodeID,
eventTopLevelType,
nativeEvent
);
}
}
});
module.exports = ReactNativeEventEmitter;

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeGlobalInteractionHandler
* @flow
*/
'use strict';
var InteractionManager = require('InteractionManager');
// Interaction handle is created/cleared when responder is granted or
// released/terminated.
var interactionHandle = null;
var ReactNativeGlobalInteractionHandler = {
onChange: function(numberActiveTouches: number) {
if (numberActiveTouches === 0) {
if (interactionHandle) {
InteractionManager.clearInteractionHandle(interactionHandle);
interactionHandle = null;
}
} else if (!interactionHandle) {
interactionHandle = InteractionManager.createInteractionHandle();
}
}
};
module.exports = ReactNativeGlobalInteractionHandler;

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeGlobalResponderHandler
* @flow
*/
'use strict';
var RCTUIManager = require('NativeModules').UIManager;
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactNativeGlobalResponderHandler = {
onChange: function(from: string, to: string) {
if (to !== null) {
RCTUIManager.setJSResponder(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(to)
);
} else {
RCTUIManager.clearJSResponder();
}
}
};
module.exports = ReactNativeGlobalResponderHandler;

View File

@@ -0,0 +1,241 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeMount
* @flow
*/
'use strict';
var RCTUIManager = require('NativeModules').UIManager;
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
var ReactUpdates = require('ReactUpdates');
var emptyObject = require('emptyObject');
var instantiateReactComponent = require('instantiateReactComponent');
var invariant = require('invariant');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
function instanceNumberToChildRootID(rootNodeID, instanceNumber) {
return rootNodeID + '[' + instanceNumber + ']';
}
/**
* Mounts this component and inserts it into the DOM.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {number} rootID ID of the root node.
* @param {number} container container element to mount into.
* @param {ReactReconcileTransaction} transaction
*/
function mountComponentIntoNode(
componentInstance,
rootID,
container,
transaction) {
var markup = ReactReconciler.mountComponent(
componentInstance, rootID, transaction, emptyObject
);
componentInstance._isTopLevel = true;
ReactNativeMount._mountImageIntoNode(markup, container);
}
/**
* Batched mount.
*
* @param {ReactComponent} componentInstance The instance to mount.
* @param {number} rootID ID of the root node.
* @param {number} container container element to mount into.
*/
function batchedMountComponentIntoNode(
componentInstance,
rootID,
container) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled();
transaction.perform(
mountComponentIntoNode,
null,
componentInstance,
rootID,
container,
transaction
);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}
/**
* As soon as `ReactMount` is refactored to not rely on the DOM, we can share
* code between the two. For now, we'll hard code the ID logic.
*/
var ReactNativeMount = {
instanceCount: 0,
_instancesByContainerID: {},
/**
* @param {ReactComponent} instance Instance to render.
* @param {containerTag} containerView Handle to native view tag
*/
renderComponent: function(
nextElement: ReactElement,
containerTag: number,
callback?: ?(() => void)
): ?ReactComponent {
var topRootNodeID = ReactNativeTagHandles.tagToRootNodeID[containerTag];
if (topRootNodeID) {
var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
if (prevComponent) {
var prevElement = prevComponent._currentElement;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
return prevComponent;
} else {
ReactNativeMount.unmountComponentAtNode(containerTag);
}
}
}
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return;
}
var topRootNodeID = ReactNativeTagHandles.allocateRootNodeIDForTag(containerTag);
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
topRootNodeID,
containerTag
);
var instance = instantiateReactComponent(nextElement);
ReactNativeMount._instancesByContainerID[topRootNodeID] = instance;
var childRootNodeID = instanceNumberToChildRootID(
topRootNodeID,
ReactNativeMount.instanceCount++
);
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
instance,
childRootNodeID,
topRootNodeID
);
var component = instance.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
},
/**
* @param {View} view View tree image.
* @param {number} containerViewID View to insert sub-view into.
*/
_mountImageIntoNode: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactComponentBrowserEnvironment',
'mountImageIntoNode',
function(mountImage, containerID) {
// Since we now know that the `mountImage` has been mounted, we can
// mark it as such.
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
mountImage.rootNodeID,
mountImage.tag
);
var addChildTags = [mountImage.tag];
var addAtIndices = [0];
RCTUIManager.manageChildren(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
null, // moveFromIndices
null, // moveToIndices
addChildTags,
addAtIndices,
null // removeAtIndices
);
}
),
/**
* Standard unmounting of the component that is rendered into `containerID`,
* but will also execute a command to remove the actual container view
* itself. This is useful when a client is cleaning up a React tree, and also
* knows that the container will no longer be needed. When executing
* asynchronously, it's easier to just have this method be the one that calls
* for removal of the view.
*/
unmountComponentAtNodeAndRemoveContainer: function(
containerTag: number
) {
ReactNativeMount.unmountComponentAtNode(containerTag);
// call back into native to remove all of the subviews from this container
RCTUIManager.removeRootView(containerTag);
},
/**
* Unmount component at container ID by iterating through each child component
* that has been rendered and unmounting it. There should just be one child
* component at this time.
*/
unmountComponentAtNode: function(containerTag: number): boolean {
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return false;
}
var containerID = ReactNativeTagHandles.tagToRootNodeID[containerTag];
var instance = ReactNativeMount._instancesByContainerID[containerID];
if (!instance) {
return false;
}
ReactNativeMount.unmountComponentFromNode(instance, containerID);
delete ReactNativeMount._instancesByContainerID[containerID];
return true;
},
/**
* Unmounts a component and sends messages back to iOS to remove its subviews.
*
* @param {ReactComponent} instance React component instance.
* @param {string} containerID ID of container we're removing from.
* @final
* @internal
* @see {ReactNativeMount.unmountComponentAtNode}
*/
unmountComponentFromNode: function(
instance: ReactComponent,
containerID: string
) {
// Call back into native to remove all of the subviews from this container
ReactReconciler.unmountComponent(instance);
var containerTag =
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID);
RCTUIManager.removeSubviewsFromContainerWithID(containerTag);
},
getNode: function<T>(id: T): T {
return id;
}
};
ReactNativeMount.renderComponent = ReactPerf.measure(
'ReactMount',
'_renderNewRootComponent',
ReactNativeMount.renderComponent
);
module.exports = ReactNativeMount;

View File

@@ -0,0 +1,282 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeBaseComponent
* @flow
*/
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
var ReactNativeComponentMixin = require('ReactNativeComponentMixin');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
var ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactMultiChild = require('ReactMultiChild');
var RCTUIManager = require('NativeModules').UIManager;
var styleDiffer = require('styleDiffer');
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
var diffRawProperties = require('diffRawProperties');
var flattenStyle = require('flattenStyle');
var precomputeStyle = require('precomputeStyle');
var warning = require('warning');
var registrationNames = ReactNativeEventEmitter.registrationNames;
var putListener = ReactNativeEventEmitter.putListener;
var deleteAllListeners = ReactNativeEventEmitter.deleteAllListeners;
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
}
/**
* @constructor ReactNativeBaseComponent
* @extends ReactComponent
* @extends ReactMultiChild
* @param {!object} UIKit View Configuration.
*/
var ReactNativeBaseComponent = function(
viewConfig: ReactNativeBaseComponentViewConfig
) {
this.viewConfig = viewConfig;
};
/**
* Generates and caches arrays of the form:
*
* [0, 1, 2, 3]
* [0, 1, 2, 3, 4]
* [0, 1]
*
* @param {number} size Size of array to generate.
* @return {Array<number>} Array with values that mirror the index.
*/
var cachedIndexArray = function(size) {
var cachedResult = cachedIndexArray._cache[size];
if (!cachedResult) {
var arr = [];
for (var i = 0; i < size; i++) {
arr[i] = i;
}
return cachedIndexArray._cache[size] = arr;
} else {
return cachedResult;
}
};
cachedIndexArray._cache = {};
/**
* Mixin for containers that contain UIViews. NOTE: markup is rendered markup
* which is a `viewID` ... see the return value for `mountComponent` !
*/
ReactNativeBaseComponent.Mixin = {
getPublicInstance: function() {
// TODO: This should probably use a composite wrapper
return this;
},
construct: function(element) {
this._currentElement = element;
},
unmountComponent: function() {
deleteAllListeners(this._rootNodeID);
this.unmountChildren();
this._rootNodeID = null;
},
/**
* Every native component is responsible for allocating its own `tag`, and
* issuing the native `createView` command. But it is not responsible for
* recording the fact that its own `rootNodeID` is associated with a
* `nodeHandle`. Only the code that actually adds its `nodeHandle` (`tag`) as
* a child of a container can confidently record that in
* `ReactNativeTagHandles`.
*/
initializeChildren: function(children, containerTag, transaction, context) {
var mountImages = this.mountChildren(children, transaction, context);
// In a well balanced tree, half of the nodes are in the bottom row and have
// no children - let's avoid calling out to the native bridge for a large
// portion of the children.
if (mountImages.length) {
var indexes = cachedIndexArray(mountImages.length);
// TODO: Pool these per platform view class. Reusing the `mountImages`
// array would likely be a jit deopt.
var createdTags = [];
for (var i = 0; i < mountImages.length; i++) {
var mountImage = mountImages[i];
var childTag = mountImage.tag;
var childID = mountImage.rootNodeID;
warning(
mountImage && mountImage.rootNodeID && mountImage.tag,
'Mount image returned does not have required data'
);
ReactNativeTagHandles.associateRootNodeIDWithMountedNodeHandle(
childID,
childTag
);
createdTags[i] = mountImage.tag;
}
RCTUIManager
.manageChildren(containerTag, null, null, createdTags, indexes, null);
}
},
/**
* Beware, this function has side effect to store this.previousFlattenedStyle!
*
* @param {!object} prevProps Previous properties
* @param {!object} nextProps Next properties
* @param {!object} validAttributes Set of valid attributes and how they
* should be diffed
*/
computeUpdatedProperties: function(prevProps, nextProps, validAttributes) {
if (__DEV__) {
for (var key in nextProps) {
if (nextProps.hasOwnProperty(key) &&
nextProps[key] &&
validAttributes[key]) {
deepFreezeAndThrowOnMutationInDev(nextProps[key]);
}
}
}
var updatePayload = diffRawProperties(
null, // updatePayload
prevProps,
nextProps,
validAttributes
);
// The style property is a deeply nested element which includes numbers
// to represent static objects. Most of the time, it doesn't change across
// renders, so it's faster to spend the time checking if it is different
// before actually doing the expensive flattening operation in order to
// compute the diff.
if (styleDiffer(nextProps.style, prevProps.style)) {
var nextFlattenedStyle = precomputeStyle(flattenStyle(nextProps.style));
updatePayload = diffRawProperties(
updatePayload,
this.previousFlattenedStyle,
nextFlattenedStyle,
ReactNativeStyleAttributes
);
this.previousFlattenedStyle = nextFlattenedStyle;
}
return updatePayload;
},
/**
* Updates the component's currently mounted representation.
*
* @param {object} nextElement
* @param {ReactReconcileTransaction} transaction
* @param {object} context
* @internal
*/
receiveComponent: function(nextElement, transaction, context) {
var prevElement = this._currentElement;
this._currentElement = nextElement;
var updatePayload = this.computeUpdatedProperties(
prevElement.props,
nextElement.props,
this.viewConfig.validAttributes
);
if (updatePayload) {
RCTUIManager.updateView(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(this._rootNodeID),
this.viewConfig.uiViewClassName,
updatePayload
);
}
this._reconcileListenersUponUpdate(
prevElement.props,
nextElement.props
);
this.updateChildren(nextElement.props.children, transaction, context);
},
/**
* @param {object} initialProps Native component props.
*/
_registerListenersUponCreation: function(initialProps) {
for (var key in initialProps) {
// NOTE: The check for `!props[key]`, is only possible because this method
// registers listeners the *first* time a component is created.
if (registrationNames[key] && initialProps[key]) {
var listener = initialProps[key];
putListener(this._rootNodeID, key, listener);
}
}
},
/**
* Reconciles event listeners, adding or removing if necessary.
* @param {object} prevProps Native component props including events.
* @param {object} nextProps Next native component props including events.
*/
_reconcileListenersUponUpdate: function(prevProps, nextProps) {
for (var key in nextProps) {
if (registrationNames[key] && (nextProps[key] != prevProps[key])) {
putListener(this._rootNodeID, key, nextProps[key]);
}
}
},
/**
* @param {string} rootID Root ID of this subtree.
* @param {Transaction} transaction For creating/updating.
* @return {string} Unique iOS view tag.
*/
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
var tag = ReactNativeTagHandles.allocateTag();
this.previousFlattenedStyle = {};
var updatePayload = this.computeUpdatedProperties(
{}, // previous props
this._currentElement.props, // next props
this.viewConfig.validAttributes
);
RCTUIManager.createView(tag, this.viewConfig.uiViewClassName, updatePayload);
this._registerListenersUponCreation(this._currentElement.props);
this.initializeChildren(
this._currentElement.props.children,
tag,
transaction,
context
);
return {
rootNodeID: rootID,
tag: tag
};
}
};
/**
* Order of mixins is important. ReactNativeBaseComponent overrides methods in
* ReactMultiChild.
*/
Object.assign(
ReactNativeBaseComponent.prototype,
ReactMultiChild.Mixin,
ReactNativeBaseComponent.Mixin,
NativeMethodsMixin,
ReactNativeComponentMixin
);
module.exports = ReactNativeBaseComponent;

View File

@@ -0,0 +1,104 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeReconcileTransaction
* @flow
*/
"use strict";
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var Transaction = require('Transaction');
/**
* Provides a `CallbackQueue` queue for collecting `onDOMReady` callbacks during
* the performing of the transaction.
*/
var ON_DOM_READY_QUEUEING = {
/**
* Initializes the internal `onDOMReady` queue.
*/
initialize: function() {
this.reactMountReady.reset();
},
/**
* After DOM is flushed, invoke all registered `onDOMReady` callbacks.
*/
close: function() {
this.reactMountReady.notifyAll();
}
};
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
/**
* Currently:
* - The order that these are listed in the transaction is critical:
* - Suppresses events.
* - Restores selection range.
*
* Future:
* - Restore document/overflow scroll positions that were unintentionally
* modified via DOM insertions above the top viewport boundary.
* - Implement/integrate with customized constraint based layout system and keep
* track of which dimensions must be remeasured.
*
* @class ReactNativeReconcileTransaction
*/
function ReactNativeReconcileTransaction() {
this.reinitializeTransaction();
this.reactMountReady = CallbackQueue.getPooled(null);
}
var Mixin = {
/**
* @see Transaction
* @abstract
* @final
* @return {array<object>} List of operation wrap proceedures.
* TODO: convert to array<TransactionWrapper>
*/
getTransactionWrappers: function() {
return TRANSACTION_WRAPPERS;
},
/**
* @return {object} The queue to collect `onDOMReady` callbacks with.
* TODO: convert to ReactMountReady
*/
getReactMountReady: function() {
return this.reactMountReady;
},
/**
* `PooledClass` looks for this, and will invoke this before allowing this
* instance to be resused.
*/
destructor: function() {
CallbackQueue.release(this.reactMountReady);
this.reactMountReady = null;
}
};
Object.assign(
ReactNativeReconcileTransaction.prototype,
Transaction.Mixin,
ReactNativeReconcileTransaction,
Mixin
);
PooledClass.addPoolingTo(ReactNativeReconcileTransaction);
module.exports = ReactNativeReconcileTransaction;

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeStyleAttributes
* @flow
*/
'use strict';
var ImageStylePropTypes = require('ImageStylePropTypes');
var TextStylePropTypes = require('TextStylePropTypes');
var ViewStylePropTypes = require('ViewStylePropTypes');
var keyMirror = require('keyMirror');
var matricesDiffer = require('matricesDiffer');
var sizesDiffer = require('sizesDiffer');
var ReactNativeStyleAttributes = {
...keyMirror(ViewStylePropTypes),
...keyMirror(TextStylePropTypes),
...keyMirror(ImageStylePropTypes),
};
ReactNativeStyleAttributes.transformMatrix = { diff: matricesDiffer };
ReactNativeStyleAttributes.shadowOffset = { diff: sizesDiffer };
module.exports = ReactNativeStyleAttributes;

View File

@@ -0,0 +1,101 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTagHandles
* @flow
*/
'use strict';
var invariant = require('invariant');
var warning = require('warning');
/**
* Keeps track of allocating and associating native "tags" which are numeric,
* unique view IDs. All the native tags are negative numbers, to avoid
* collisions, but in the JS we keep track of them as positive integers to store
* them effectively in Arrays. So we must refer to them as "inverses" of the
* native tags (that are * normally negative).
*
* It *must* be the case that every `rootNodeID` always maps to the exact same
* `tag` forever. The easiest way to accomplish this is to never delete
* anything from this table.
* Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
* unmount a component with a `rootNodeID`, then mount a new one in its place,
*/
var INITIAL_TAG_COUNT = 1;
var ReactNativeTagHandles = {
tagsStartAt: INITIAL_TAG_COUNT,
tagCount: INITIAL_TAG_COUNT,
allocateTag: function(): number {
// Skip over root IDs as those are reserved for native
while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
ReactNativeTagHandles.tagCount++;
}
var tag = ReactNativeTagHandles.tagCount;
ReactNativeTagHandles.tagCount++;
return tag;
},
/**
* This associates the *last* observed *native* mounting between `rootNodeID`
* and some `tag`. This association doesn't imply that `rootNodeID` is still
* natively mounted as `tag`. The only reason why we don't clear the
* association when the `rootNodeID` is unmounted, is that we don't have a
* convenient time to disassociate them (otherwise we would).
* `unmountComponent` isn't the correct time because that doesn't imply that
* the native node has been natively unmounted.
*/
associateRootNodeIDWithMountedNodeHandle: function(
rootNodeID: ?string,
tag: ?number
) {
warning(rootNodeID && tag, 'Root node or tag is null when associating');
if (rootNodeID && tag) {
ReactNativeTagHandles.tagToRootNodeID[tag] = rootNodeID;
ReactNativeTagHandles.rootNodeIDToTag[rootNodeID] = tag;
}
},
allocateRootNodeIDForTag: function(tag: number): string {
invariant(
this.reactTagIsNativeTopRootID(tag),
'Expect a native root tag, instead got ', tag
);
return '.r[' + tag + ']{TOP_LEVEL}';
},
reactTagIsNativeTopRootID: function(reactTag: number): bool {
// We reserve all tags that are 1 mod 10 for native root views
return reactTag % 10 === 1;
},
/**
* Returns the native `nodeHandle` (`tag`) that was most recently *natively*
* mounted at the `rootNodeID`. Just because a React component has been
* mounted, that doesn't mean that its native node has been mounted. The
* native node is mounted when we actually make the call to insert the
* `nodeHandle` (`tag`) into the native hierarchy.
*
* @param {string} rootNodeID Root node ID to find most recently mounted tag
* for. Again, this doesn't imply that it is still currently mounted.
* @return {number} Tag ID of native view for most recent mounting of
* `rootNodeID`.
*/
mostRecentMountedNodeHandleForRootNodeID: function(
rootNodeID: string
): number {
return ReactNativeTagHandles.rootNodeIDToTag[rootNodeID];
},
tagToRootNodeID: ([] : Array<string>),
rootNodeIDToTag: ({} : {[key: string]: number})
};
module.exports = ReactNativeTagHandles;

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeTextComponent
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var RCTUIManager = require('NativeModules').UIManager;
var assign = require('Object.assign');
var ReactNativeTextComponent = function(props) {
// This constructor and its argument is currently used by mocks.
};
assign(ReactNativeTextComponent.prototype, {
construct: function(text) {
// This is really a ReactText (ReactNode), not a ReactElement
this._currentElement = text;
this._stringText = '' + text;
this._rootNodeID = null;
},
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
var tag = ReactNativeTagHandles.allocateTag();
RCTUIManager.createView(tag, 'RCTRawText', {text: this._stringText});
return {
rootNodeID: rootID,
tag: tag,
};
},
receiveComponent: function(nextText, transaction, context) {
if (nextText !== this._currentElement) {
this._currentElement = nextText;
var nextStringText = '' + nextText;
if (nextStringText !== this._stringText) {
this._stringText = nextStringText;
RCTUIManager.updateView(
ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(
this._rootNodeID
),
'RCTRawText',
{text: this._stringText}
);
}
}
},
unmountComponent: function() {
this._currentElement = null;
this._stringText = null;
this._rootNodeID = null;
}
});
module.exports = ReactNativeTextComponent;

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactNativeViewAttributes
* @flow
*/
'use strict';
var merge = require('merge');
var ReactNativeViewAttributes = {};
ReactNativeViewAttributes.UIView = {
pointerEvents: true,
accessible: true,
accessibilityLabel: true,
testID: true,
onLayout: true,
};
ReactNativeViewAttributes.RCTView = merge(
ReactNativeViewAttributes.UIView, {
// This is a special performance property exposed by RCTView and useful for
// scrolling content when there are many subviews, most of which are offscreen.
// For this property to be effective, it must be applied to a view that contains
// many subviews that extend outside its bound. The subviews must also have
// overflow: hidden, as should the containing view (or one of its superviews).
removeClippedSubviews: true,
});
module.exports = ReactNativeViewAttributes;

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule createReactNativeComponentClass
* @flow
*/
"use strict";
var ReactElement = require('ReactElement');
var ReactNativeBaseComponent = require('ReactNativeBaseComponent');
// See also ReactNativeBaseComponent
type ReactNativeBaseComponentViewConfig = {
validAttributes: Object;
uiViewClassName: string;
}
/**
* @param {string} config iOS View configuration.
* @private
*/
var createReactNativeComponentClass = function(
viewConfig: ReactNativeBaseComponentViewConfig
): Function { // returning Function is lossy :/
var Constructor = function(element) {
this._currentElement = element;
this._rootNodeID = null;
this._renderedChildren = null;
this.previousFlattenedStyle = null;
};
Constructor.displayName = viewConfig.uiViewClassName;
Constructor.prototype = new ReactNativeBaseComponent(viewConfig);
return Constructor;
};
module.exports = createReactNativeComponentClass;