2015-02-20 04:10:52 +00:00
|
|
|
/**
|
|
|
|
* Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* @providesModule ReactIOSMount
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2015-03-18 05:22:03 +00:00
|
|
|
var RCTUIManager = require('NativeModules').UIManager;
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
var ReactIOSTagHandles = require('ReactIOSTagHandles');
|
|
|
|
var ReactPerf = require('ReactPerf');
|
2015-03-07 00:29:59 +00:00
|
|
|
var ReactReconciler = require('ReactReconciler');
|
|
|
|
var ReactUpdates = require('ReactUpdates');
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-07 00:29:59 +00:00
|
|
|
var emptyObject = require('emptyObject');
|
2015-02-20 04:10:52 +00:00
|
|
|
var instantiateReactComponent = require('instantiateReactComponent');
|
|
|
|
var invariant = require('invariant');
|
|
|
|
|
|
|
|
var TOP_ROOT_NODE_IDS = {};
|
|
|
|
|
|
|
|
function instanceNumberToChildRootID(rootNodeID, instanceNumber) {
|
|
|
|
return rootNodeID + '[' + instanceNumber + ']';
|
|
|
|
}
|
|
|
|
|
2015-03-07 00:29:59 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
ReactIOSMount._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);
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
/**
|
|
|
|
* 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 ReactIOSMount = {
|
|
|
|
instanceCount: 0,
|
|
|
|
|
|
|
|
_instancesByContainerID: {},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ReactComponent} instance Instance to render.
|
|
|
|
* @param {containerTag} containerView Handle to native view tag
|
|
|
|
*/
|
|
|
|
renderComponent: function(descriptor, containerTag) {
|
|
|
|
var instance = instantiateReactComponent(descriptor);
|
|
|
|
|
|
|
|
if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) {
|
|
|
|
console.error('You cannot render into anything but a top root');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var topRootNodeID = ReactIOSTagHandles.allocateRootNodeIDForTag(containerTag);
|
|
|
|
ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
|
|
|
|
topRootNodeID,
|
|
|
|
containerTag
|
|
|
|
);
|
|
|
|
TOP_ROOT_NODE_IDS[topRootNodeID] = true;
|
|
|
|
|
|
|
|
var childRootNodeID = instanceNumberToChildRootID(
|
|
|
|
topRootNodeID,
|
|
|
|
ReactIOSMount.instanceCount++
|
|
|
|
);
|
|
|
|
ReactIOSMount._instancesByContainerID[topRootNodeID] = instance;
|
2015-03-07 00:29:59 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
);
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
2015-03-07 00:29:59 +00:00
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
ReactIOSTagHandles.associateRootNodeIDWithMountedNodeHandle(
|
|
|
|
mountImage.rootNodeID,
|
|
|
|
mountImage.tag
|
|
|
|
);
|
|
|
|
var addChildTags = [mountImage.tag];
|
|
|
|
var addAtIndices = [0];
|
2015-03-17 10:08:46 +00:00
|
|
|
RCTUIManager.manageChildren(
|
2015-03-07 00:29:59 +00:00
|
|
|
ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID),
|
|
|
|
null, // moveFromIndices
|
|
|
|
null, // moveToIndices
|
|
|
|
addChildTags,
|
|
|
|
addAtIndices,
|
|
|
|
null // removeAtIndices
|
|
|
|
);
|
|
|
|
}
|
|
|
|
),
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
|
|
|
ReactIOSMount.unmountComponentAtNode(containerTag);
|
|
|
|
// call back into native to remove all of the subviews from this container
|
2015-03-17 10:08:46 +00:00
|
|
|
RCTUIManager.removeRootView(containerTag);
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
|
|
|
var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag];
|
|
|
|
|
|
|
|
invariant(
|
|
|
|
TOP_ROOT_NODE_IDS[containerID],
|
|
|
|
'We only currently support removing components from the root node'
|
|
|
|
);
|
|
|
|
var instance = ReactIOSMount._instancesByContainerID[containerID];
|
|
|
|
if (!instance) {
|
|
|
|
console.error('Tried to unmount a component that does not exist');
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ReactIOSMount.unmountComponentFromNode(instance, containerID);
|
|
|
|
delete ReactIOSMount._instancesByContainerID[containerID];
|
|
|
|
delete TOP_ROOT_NODE_IDS[containerID];
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unmounts a component and sends messages back to iOS to remove its subviews.
|
|
|
|
*
|
|
|
|
* @param {ReactComponent} instance React component instance.
|
|
|
|
* @param {int} containerID ID of container we're removing from.
|
|
|
|
* @final
|
|
|
|
* @internal
|
|
|
|
* @see {ReactIOSMount.unmountComponentAtNode}
|
|
|
|
*/
|
|
|
|
unmountComponentFromNode: function(instance, containerID) {
|
|
|
|
// call back into native to remove all of the subviews from this container
|
|
|
|
instance.unmountComponent();
|
|
|
|
var containerTag =
|
|
|
|
ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID);
|
2015-03-17 10:08:46 +00:00
|
|
|
RCTUIManager.removeSubviewsFromContainerWithID(containerTag);
|
2015-02-20 04:10:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getNode: function(id) {
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ReactIOSMount.renderComponent = ReactPerf.measure(
|
|
|
|
'ReactMount',
|
|
|
|
'_renderNewRootComponent',
|
|
|
|
ReactIOSMount.renderComponent
|
|
|
|
);
|
|
|
|
|
|
|
|
module.exports = ReactIOSMount;
|