<-- Supplied `container`.
+ *
<-- Rendered reactRoot of React component.
+ * // ...
+ *
+ *
+ *
+ * Inside of `container`, the first element rendered is the "reactRoot".
+ */
+var ReactMount = {
+
+ /** Time spent generating markup. */
+ totalInstantiationTime: 0,
+
+ /** Time spent inserting markup into the DOM. */
+ totalInjectionTime: 0,
+
+ /** Whether support for touch events should be initialized. */
+ useTouchEvents: false,
+
+ /**
+ * This is a hook provided to support rendering React components while
+ * ensuring that the apparent scroll position of its `container` does not
+ * change.
+ *
+ * @param {DOMElement} container The `container` being rendered into.
+ * @param {function} renderCallback This must be called once to do the render.
+ */
+ scrollMonitor: function(container, renderCallback) {
+ renderCallback();
+ },
+
+ /**
+ * Ensures tht the top-level event delegation listener is set up. This will be
+ * invoked some time before the first time any React component is rendered.
+ *
+ * @param {object} TopLevelCallbackCreator
+ * @private
+ */
+ prepareTopLevelEvents: function(TopLevelCallbackCreator) {
+ ReactEvent.ensureListening(
+ ReactMount.useTouchEvents,
+ TopLevelCallbackCreator
+ );
+ },
+
+ /**
+ * Renders a React component into the DOM in the supplied `container`.
+ *
+ * If the React component was previously rendered into `container`, this will
+ * perform an update on it and only mutate the DOM as necessary to reflect the
+ * latest React component.
+ *
+ * @param {ReactComponent} nextComponent Component instance to render.
+ * @param {DOMElement} container DOM element to render into.
+ * @return {ReactComponent} Component instance rendered in `container`.
+ */
+ renderComponent: function(nextComponent, container) {
+ var prevComponent = instanceByReactRootID[getReactRootID(container)];
+ if (prevComponent) {
+ var nextProps = nextComponent.props;
+ ReactMount.scrollMonitor(container, function() {
+ prevComponent.replaceProps(nextProps);
+ });
+ return prevComponent;
+ }
+
+ ReactMount.prepareTopLevelEvents(ReactEventTopLevelCallback);
+
+ var reactRootID = ReactMount.registerContainer(container);
+ instanceByReactRootID[reactRootID] = nextComponent;
+ nextComponent.mountComponentIntoNode(reactRootID, container);
+ return nextComponent;
+ },
+
+ /**
+ * Creates a function that accepts a `container` and renders the supplied
+ * React component instance into it.
+ *
+ * var renderInto = ReactMount.createComponentRenderer(component);
+ * // ...
+ * var component = renderInto($('container'));
+ *
+ * @param {ReactComponent} component Component instance to render.
+ * @return {function(DOMElement): ReactComponent}
+ */
+ createComponentRenderer: function(component) {
+ return function(container) {
+ return ReactMount.renderComponent(component, container);
+ };
+ },
+
+ /**
+ * Constructs a component instance of `constructor` with `initialProps` and
+ * renders it into the supplied `container`.
+ *
+ * @param {function} constructor React component constructor.
+ * @param {?object} props Initial props of the component instance.
+ * @param {DOMElement} container DOM element to render into.
+ * @return {ReactComponent} Component instance rendered in `container`.
+ */
+ constructAndRenderComponent: function(constructor, props, container) {
+ return ReactMount.renderComponent(constructor(props), container);
+ },
+
+ /**
+ * Constructs a component instance of `constructor` with `initialProps` and
+ * renders it into a container node identified by supplied `id`.
+ *
+ * @param {function} componentConstructor React component constructor
+ * @param {?object} props Initial props of the component instance.
+ * @param {string} id ID of the DOM element to render into.
+ * @return {ReactComponent} Component instance rendered in the container node.
+ */
+ constructAndRenderComponentByID: function(constructor, props, id) {
+ return ReactMount.constructAndRenderComponent(constructor, props, $(id));
+ },
+
+ /**
+ * Registers a container node into which React components will be rendered.
+ * This also creates the "reatRoot" ID that will be assigned to the element
+ * rendered within.
+ *
+ * @param {DOMElement} container DOM element to register as a container.
+ * @return {string} The "reactRoot" ID of elements rendered within.
+ */
+ registerContainer: function(container) {
+ var reactRootID = getReactRootID(container);
+ if (reactRootID) {
+ // If one exists, make sure it is a valid "reactRoot" ID.
+ reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID);
+ }
+ if (!reactRootID) {
+ // No valid "reactRoot" ID found, create one.
+ reactRootID = ReactInstanceHandles.getReactRootID(
+ globalMountPointCounter++
+ );
+ }
+ containersByReactRootID[reactRootID] = container;
+ return reactRootID;
+ },
+
+ /**
+ * Unmounts and destroys the React component rendered in the `container`.
+ *
+ * @param {DOMElement} container DOM element containing a React component.
+ */
+ unmountAndReleaseReactRootNode: function(container) {
+ var reactRootID = getReactRootID(container);
+ var component = instanceByReactRootID[reactRootID];
+ // TODO: Consider throwing if no `component` was found.
+ component.unmountComponentFromNode(container);
+ delete instanceByReactRootID[reactRootID];
+ delete containersByReactRootID[reactRootID];
+ },
+
+ /**
+ * Finds the container DOM element that contains React component to which the
+ * supplied DOM `id` belongs.
+ *
+ * @param {string} id The ID of an element rendered by a React component.
+ * @return {?DOMElement} DOM element that contains the `id`.
+ */
+ findReactContainerForID: function(id) {
+ var reatRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
+ // TODO: Consider throwing if `id` is not a valid React element ID.
+ return containersByReactRootID[reatRootID];
+ },
+
+ /**
+ * Given the ID of a DOM node rendered by a React component, finds the root
+ * DOM node of the React component.
+ *
+ * @param {string} id ID of a DOM node in the React component.
+ * @return {?DOMElement} Root DOM node of the React component.
+ */
+ findReactRenderedDOMNodeSlow: function(id) {
+ var reactRoot = ReactMount.findReactContainerForID(id);
+ return ReactInstanceHandles.findComponentRoot(reactRoot, id);
+ }
+
+};
+
+module.exports = ReactMount;
diff --git a/React/ReactMultiChild.js b/React/ReactMultiChild.js
new file mode 100644
index 0000000000..6e0b7b32e3
--- /dev/null
+++ b/React/ReactMultiChild.js
@@ -0,0 +1,208 @@
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @providesModule ReactMultiChild
+ */
+
+"use strict";
+
+var ReactComponent = require("./ReactComponent");
+
+/**
+ * Given a `curChild` and `newChild`, determines if `curChild` should be managed
+ * as it exists, as opposed to being destroyed and/or replaced.
+ * @param {?ReactComponent} curChild
+ * @param {?ReactComponent} newChild
+ * @return {!boolean} Whether or not `curChild` should be updated with
+ * `newChild`'s props
+ */
+function shouldManageExisting(curChild, newChild) {
+ return curChild && newChild && curChild.constructor === newChild.constructor;
+}
+
+/**
+ * `ReactMultiChild` provides common functionality for components that have
+ * multiple children. Standard `ReactCompositeComponent`s do not currently have
+ * multiple children. `ReactNativeComponent`s do, however. Other specially
+ * reconciled components will also have multiple children. Contains three
+ * internally used properties that are used to keep track of state throughout
+ * the `updateMultiChild` process.
+ *
+ * @class ReactMultiChild
+ */
+
+/**
+ * @lends `ReactMultiChildMixin`.
+ */
+var ReactMultiChildMixin = {
+
+ enqueueMarkupAt: function(markup, insertAt) {
+ this.domOperations = this.domOperations || [];
+ this.domOperations.push({insertMarkup: markup, finalIndex: insertAt});
+ },
+
+ enqueueMove: function(originalIndex, finalIndex) {
+ this.domOperations = this.domOperations || [];
+ this.domOperations.push({moveFrom: originalIndex, finalIndex: finalIndex});
+ },
+
+ enqueueUnmountChildByName: function(name, removeChild) {
+ if (ReactComponent.isValidComponent(removeChild)) {
+ this.domOperations = this.domOperations || [];
+ this.domOperations.push({removeAt: removeChild._domIndex});
+ removeChild.unmountComponent && removeChild.unmountComponent();
+ delete this._renderedChildren[name];
+ }
+ },
+
+ /**
+ * Process any pending DOM operations that have been accumulated when updating
+ * the UI. By default, we execute the injected `DOMIDOperations` module's
+ * `manageChildrenByParentID` which does executes the DOM operations without
+ * any animation. It can be used as a reference implementation for special
+ * animation based implementations.
+ *
+ * @abstract
+ */
+ processChildDOMOperationsQueue: function() {
+ if (this.domOperations) {
+ ReactComponent.DOMIDOperations
+ .manageChildrenByParentID(this._rootNodeID, this.domOperations);
+ this.domOperations = null;
+ }
+ },
+
+ unmountMultiChild: function() {
+ var renderedChildren = this._renderedChildren;
+ for (var name in renderedChildren) {
+ if (renderedChildren.hasOwnProperty(name) && renderedChildren[name]) {
+ var renderedChild = renderedChildren[name];
+ renderedChild.unmountComponent && renderedChild.unmountComponent();
+ }
+ }
+ this._renderedChildren = null;
+ },
+
+ /**
+ * Generates markup for a component that holds multiple children. #todo: Allow
+ * all `ReactMultiChildMixin`s to support having arrays of children without a
+ * container node. This current implementation may assume that children exist
+ * at domIndices [0, parentNode.length].
+ *
+ * Has side effects of (likely) causing events to be registered. Also, every
+ * component instance may only be rendered once.
+ *
+ * @param {?Object} children Flattened children object.
+ * @return {!String} The rendered markup.
+ */
+ mountMultiChild: function(children, transaction) {
+ var accum = '';
+ var index = 0;
+ for (var name in children) {
+ var child = children[name];
+ if (children.hasOwnProperty(name) && child) {
+ accum += child.mountComponent(
+ this._rootNodeID + '.' + name,
+ transaction
+ );
+ child._domIndex = index;
+ index++;
+ }
+ }
+ this._renderedChildren = children; // children are in just the right form!
+ this.domOperations = null;
+ return accum;
+ },
+
+ /**
+ * Reconciles new children with old children in three phases.
+ *
+ * - Adds new content while updating existing children that should remain.
+ * - Remove children that are no longer present in the next children.
+ * - As a very last step, moves existing dom structures around.
+ * - (Comment 1) `curChildrenDOMIndex` is the largest index of the current
+ * rendered children that appears in the next children and did not need to
+ * be "moved".
+ * - (Comment 2) This is the key insight. If any non-removed child's previous
+ * index is less than `curChildrenDOMIndex` it must be moved.
+ *
+ * @param {?Object} children Flattened children object.
+ */
+ updateMultiChild: function(nextChildren, transaction) {
+ if (!nextChildren && !this._renderedChildren) {
+ return;
+ } else if (nextChildren && !this._renderedChildren) {
+ this._renderedChildren = {}; // lazily allocate backing store with nothing
+ } else if (!nextChildren && this._renderedChildren) {
+ nextChildren = {};
+ }
+ var rootDomIdDot = this._rootNodeID + '.';
+ var markupBuffer = null; // Accumulate adjacent new children markup.
+ var numPendingInsert = 0; // How many root nodes are waiting in markupBuffer
+ var loopDomIndex = 0; // Index of loop through new children.
+ var curChildrenDOMIndex = 0; // See (Comment 1)
+ for (var name in nextChildren) {
+ if (!nextChildren.hasOwnProperty(name)) {continue;}
+ var curChild = this._renderedChildren[name];
+ var nextChild = nextChildren[name];
+ if (shouldManageExisting(curChild, nextChild)) {
+ if (markupBuffer) {
+ this.enqueueMarkupAt(markupBuffer, loopDomIndex - numPendingInsert);
+ markupBuffer = null;
+ }
+ numPendingInsert = 0;
+ if (curChild._domIndex < curChildrenDOMIndex) { // (Comment 2)
+ this.enqueueMove(curChild._domIndex, loopDomIndex);
+ }
+ curChildrenDOMIndex = Math.max(curChild._domIndex, curChildrenDOMIndex);
+ !nextChild.props.isStatic &&
+ curChild.receiveProps(nextChild.props, transaction);
+ curChild._domIndex = loopDomIndex;
+ } else {
+ if (curChild) { // !shouldUpdate && curChild => delete
+ this.enqueueUnmountChildByName(name, curChild);
+ curChildrenDOMIndex =
+ Math.max(curChild._domIndex, curChildrenDOMIndex);
+ }
+ if (nextChild) { // !shouldUpdate && nextChild => insert
+ this._renderedChildren[name] = nextChild;
+ var nextMarkup =
+ nextChild.mountComponent(rootDomIdDot + name, transaction);
+ markupBuffer = markupBuffer ? markupBuffer + nextMarkup : nextMarkup;
+ numPendingInsert++;
+ nextChild._domIndex = loopDomIndex;
+ }
+ }
+ loopDomIndex = nextChild ? loopDomIndex + 1 : loopDomIndex;
+ }
+ if (markupBuffer) {
+ this.enqueueMarkupAt(markupBuffer, loopDomIndex - numPendingInsert);
+ }
+ for (var childName in this._renderedChildren) { // from other direction
+ if (!this._renderedChildren.hasOwnProperty(childName)) { continue; }
+ var child = this._renderedChildren[childName];
+ if (child && !nextChildren[childName]) {
+ this.enqueueUnmountChildByName(childName, child);
+ }
+ }
+ this.processChildDOMOperationsQueue();
+ }
+};
+
+var ReactMultiChild = {
+ Mixin: ReactMultiChildMixin
+};
+
+module.exports = ReactMultiChild;
diff --git a/React/ReactNativeComponent.js b/React/ReactNativeComponent.js
new file mode 100644
index 0000000000..1c0cb2d54e
--- /dev/null
+++ b/React/ReactNativeComponent.js
@@ -0,0 +1,331 @@
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @providesModule ReactNativeComponent
+ * @typechecks
+ */
+
+"use strict";
+
+var CSSPropertyOperations = require("./CSSPropertyOperations");
+var DOMPropertyOperations = require("./DOMPropertyOperations");
+var ReactComponent = require("./ReactComponent");
+var ReactEvent = require("./ReactEvent");
+var ReactMultiChild = require("./ReactMultiChild");
+
+var escapeTextForBrowser = require("./escapeTextForBrowser");
+var flattenChildren = require("./flattenChildren");
+var invariant = require("./invariant");
+var keyOf = require("./keyOf");
+var merge = require("./merge");
+var mixInto = require("./mixInto");
+
+var putListener = ReactEvent.putListener;
+var registrationNames = ReactEvent.registrationNames;
+
+// For quickly matching children type, to test if can be treated as content.
+var CONTENT_TYPES = {'string': true, 'number': true};
+
+var CONTENT = keyOf({content: null});
+var DANGEROUSLY_SET_INNER_HTML = keyOf({dangerouslySetInnerHTML: null});
+var STYLE = keyOf({style: null});
+
+/**
+ * @param {?object} props
+ */
+function assertValidProps(props) {
+ if (!props) {
+ return;
+ }
+ // Note the use of `!=` which checks for null or undefined.
+ var hasChildren = props.children != null ? 1 : 0;
+ var hasContent = props.content != null ? 1 : 0;
+ var hasInnerHTML = props.dangerouslySetInnerHTML != null ? 1 : 0;
+ invariant(
+ hasChildren + hasContent + hasInnerHTML <= 1,
+ 'Can only set one of `children`, `props.content`, or ' +
+ '`props.dangerouslySetInnerHTML`.'
+ );
+ invariant(
+ props.style == null || typeof props.style === 'object',
+ 'The `style` prop expects a mapping from style properties to values, ' +
+ 'not a string.'
+ );
+}
+
+/**
+ * @constructor ReactNativeComponent
+ * @extends ReactComponent
+ * @extends ReactMultiChild
+ */
+function ReactNativeComponent(tag, omitClose) {
+ this._tagOpen = '<' + tag + ' ';
+ this._tagClose = omitClose ? '' : '' + tag + '>';
+ this.tagName = tag.toUpperCase();
+}
+
+ReactNativeComponent.Mixin = {
+
+ /**
+ * Generates root tag markup then recurses. This method has side effects and
+ * is not idempotent.
+ *
+ * @internal
+ * @param {string} rootID The root DOM ID for this node.
+ * @param {ReactReconcileTransaction} transaction
+ * @return {string} The computed markup.
+ */
+ mountComponent: function(rootID, transaction) {
+ ReactComponent.Mixin.mountComponent.call(this, rootID, transaction);
+ assertValidProps(this.props);
+ return (
+ this._createOpenTagMarkup() +
+ this._createContentMarkup(transaction) +
+ this._tagClose
+ );
+ },
+
+ /**
+ * Creates markup for the open tag and all attributes.
+ *
+ * This method has side effects because events get registered.
+ *
+ * Iterating over object properties is faster than iterating over arrays.
+ * @see http://jsperf.com/obj-vs-arr-iteration
+ *
+ * @private
+ * @return {string} Markup of opening tag.
+ */
+ _createOpenTagMarkup: function() {
+ var props = this.props;
+ var ret = this._tagOpen;
+
+ for (var propKey in props) {
+ if (!props.hasOwnProperty(propKey)) {
+ continue;
+ }
+ var propValue = props[propKey];
+ if (propValue == null) {
+ continue;
+ }
+ if (registrationNames[propKey]) {
+ putListener(this._rootNodeID, propKey, propValue);
+ } else {
+ if (propKey === STYLE) {
+ if (propValue) {
+ propValue = props.style = merge(props.style);
+ }
+ propValue = CSSPropertyOperations.createMarkupForStyles(propValue);
+ }
+ var markup =
+ DOMPropertyOperations.createMarkupForProperty(propKey, propValue);
+ if (markup) {
+ ret += ' ' + markup;
+ }
+ }
+ }
+
+ return ret + ' id="' + this._rootNodeID + '">';
+ },
+
+ /**
+ * Creates markup for the content between the tags.
+ *
+ * @private
+ * @param {ReactReconcileTransaction} transaction
+ * @return {string} Content markup.
+ */
+ _createContentMarkup: function(transaction) {
+ // Intentional use of != to avoid catching zero/false.
+ var innerHTML = this.props.dangerouslySetInnerHTML;
+ if (innerHTML != null) {
+ if (innerHTML.__html != null) {
+ return innerHTML.__html;
+ }
+ } else {
+ var contentToUse = this.props.content != null ? this.props.content :
+ CONTENT_TYPES[typeof this.props.children] ? this.props.children : null;
+ var childrenToUse = contentToUse != null ? null : this.props.children;
+ if (contentToUse != null) {
+ return escapeTextForBrowser(contentToUse);
+ } else if (childrenToUse != null) {
+ return this.mountMultiChild(
+ flattenChildren(childrenToUse),
+ transaction
+ );
+ }
+ }
+ return '';
+ },
+
+ /**
+ * Controls a native DOM component after it has already been allocated and
+ * attached to the DOM. Reconciles the root DOM node, then recurses.
+ *
+ * @internal
+ * @param {object} nextProps
+ * @param {ReactReconcileTransaction} transaction
+ */
+ receiveProps: function(nextProps, transaction) {
+ invariant(
+ this._rootNodeID,
+ 'Trying to control a native dom element without a backing id'
+ );
+ assertValidProps(nextProps);
+ ReactComponent.Mixin.receiveProps.call(this, nextProps, transaction);
+ this._updateDOMProperties(nextProps);
+ this._updateDOMChildren(nextProps, transaction);
+ this.props = nextProps;
+ },
+
+ /**
+ * Reconciles the properties by detecting differences in property values and
+ * updating the DOM as necessary. This function is probably the single most
+ * critical path for performance optimization.
+ *
+ * TODO: Benchmark whether checking for changed values in memory actually
+ * improves performance (especially statically positioned elements).
+ * TODO: Benchmark the effects of putting this at the top since 99% of props
+ * do not change for a given reconciliation.
+ * TODO: Benchmark areas that can be improved with caching.
+ *
+ * @private
+ * @param {object} nextProps
+ */
+ _updateDOMProperties: function(nextProps) {
+ var lastProps = this.props;
+ for (var propKey in nextProps) {
+ var nextProp = nextProps[propKey];
+ var lastProp = lastProps[propKey];
+ if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp) {
+ continue;
+ }
+ if (propKey === STYLE) {
+ if (nextProp) {
+ nextProp = nextProps.style = merge(nextProp);
+ }
+ var styleUpdates;
+ for (var styleName in nextProp) {
+ if (!nextProp.hasOwnProperty(styleName)) {
+ continue;
+ }
+ if (!lastProp || lastProp[styleName] !== nextProp[styleName]) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = nextProp[styleName];
+ }
+ }
+ if (styleUpdates) {
+ ReactComponent.DOMIDOperations.updateStylesByID(
+ this._rootNodeID,
+ styleUpdates
+ );
+ }
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ var lastHtml = lastProp && lastProp.__html;
+ var nextHtml = nextProp && nextProp.__html;
+ if (lastHtml !== nextHtml) {
+ ReactComponent.DOMIDOperations.updateInnerHTMLByID(
+ this._rootNodeID,
+ nextProp
+ );
+ }
+ } else if (propKey === CONTENT) {
+ ReactComponent.DOMIDOperations.updateTextContentByID(
+ this._rootNodeID,
+ '' + nextProp
+ );
+ } else if (registrationNames[propKey]) {
+ putListener(this._rootNodeID, propKey, nextProp);
+ } else {
+ ReactComponent.DOMIDOperations.updatePropertyByID(
+ this._rootNodeID,
+ propKey,
+ nextProp
+ );
+ }
+ }
+ },
+
+ /**
+ * Reconciles the children with the various properties that affect the
+ * children content.
+ *
+ * @param {object} nextProps
+ * @param {ReactReconcileTransaction} transaction
+ */
+ _updateDOMChildren: function(nextProps, transaction) {
+ var thisPropsContentType = typeof this.props.content;
+ var thisPropsContentEmpty =
+ this.props.content == null || thisPropsContentType === 'boolean';
+ var nextPropsContentType = typeof nextProps.content;
+ var nextPropsContentEmpty =
+ nextProps.content == null || nextPropsContentType === 'boolean';
+
+ var lastUsedContent = !thisPropsContentEmpty ? this.props.content :
+ CONTENT_TYPES[typeof this.props.children] ? this.props.children : null;
+
+ var contentToUse = !nextPropsContentEmpty ? nextProps.content :
+ CONTENT_TYPES[typeof nextProps.children] ? nextProps.children : null;
+
+ // Note the use of `!=` which checks for null or undefined.
+
+ var lastUsedChildren =
+ lastUsedContent != null ? null : this.props.children;
+ var childrenToUse = contentToUse != null ? null : nextProps.children;
+
+ if (contentToUse != null) {
+ var childrenRemoved = lastUsedChildren != null && childrenToUse == null;
+ if (childrenRemoved) {
+ this.updateMultiChild(null, transaction);
+ }
+ if (lastUsedContent !== contentToUse) {
+ ReactComponent.DOMIDOperations.updateTextContentByID(
+ this._rootNodeID,
+ '' + contentToUse
+ );
+ }
+ } else {
+ var contentRemoved = lastUsedContent != null && contentToUse == null;
+ if (contentRemoved) {
+ ReactComponent.DOMIDOperations.updateTextContentByID(
+ this._rootNodeID,
+ ''
+ );
+ }
+ this.updateMultiChild(flattenChildren(nextProps.children), transaction);
+ }
+ },
+
+ /**
+ * Destroys all event registrations for this instance. Does not remove from
+ * the DOM. That must be done by the parent.
+ *
+ * @internal
+ */
+ unmountComponent: function() {
+ ReactComponent.Mixin.unmountComponent.call(this);
+ this.unmountMultiChild();
+ ReactEvent.deleteAllListeners(this._rootNodeID);
+ }
+
+};
+
+mixInto(ReactNativeComponent, ReactComponent.Mixin);
+mixInto(ReactNativeComponent, ReactNativeComponent.Mixin);
+mixInto(ReactNativeComponent, ReactMultiChild.Mixin);
+
+module.exports = ReactNativeComponent;
diff --git a/React/ReactOnDOMReady.js b/React/ReactOnDOMReady.js
new file mode 100644
index 0000000000..a425ff03be
--- /dev/null
+++ b/React/ReactOnDOMReady.js
@@ -0,0 +1,95 @@
+/**
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @providesModule ReactOnDOMReady
+ */
+
+"use strict";
+
+var PooledClass = require("./PooledClass");
+
+var mixInto = require("./mixInto");
+
+/**
+ * A specialized pseudo-event module to help keep track of components waiting to
+ * be notified when their DOM representations are available for use.
+ *
+ * This implements `PooledClass`, so you should never need to instantiate this.
+ * Instead, use `ReactOnDOMReady.getPooled()`.
+ *
+ * @param {?array