Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/React.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var ReactTextComponent = require('ReactTextComponent');
ReactDefaultInjection.inject();

var React = {
EventPluginRegistry: require('EventPluginRegistry'),
DOM: ReactDOM,
PropTypes: ReactPropTypes,
initializeTouchEvents: function(shouldUseTouch) {
Expand Down
10 changes: 7 additions & 3 deletions src/core/ReactComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ var ReactComponent = {
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy.
* @return {?string} Rendered markup to be inserted into the DOM.
* @return {?ReactDOMMountImage} Mount image to be inserted into the DOM.
* @internal
*/
mountComponent: function(rootID, transaction, mountDepth) {
Expand Down Expand Up @@ -486,8 +486,12 @@ var ReactComponent = {
container,
transaction,
shouldReuseMarkup) {
var markup = this.mountComponent(rootID, transaction, 0);
ReactComponent.mountImageIntoNode(markup, container, shouldReuseMarkup);
var mountImage = this.mountComponent(rootID, transaction, 0);
ReactComponent.mountImageIntoNode(
mountImage,
container,
shouldReuseMarkup
);
},

/**
Expand Down
23 changes: 17 additions & 6 deletions src/core/ReactComponentBrowserEnvironment.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"use strict";

var ReactDOMIDOperations = require('ReactDOMIDOperations');
var ReactDOMMountImage = require('ReactDOMMountImage');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactMount = require('ReactMount');
var ReactReconcileTransaction = require('ReactReconcileTransaction');
Expand All @@ -29,6 +30,10 @@ var getReactRootElementInContainer = require('getReactRootElementInContainer');
var invariant = require('invariant');
var mutateHTMLNodeWithMarkup = require('mutateHTMLNodeWithMarkup');

if (__DEV__) {
var validateNodeNesting = require('validateNodeNesting');
}


var ELEMENT_NODE_TYPE = 1;
var DOC_NODE_TYPE = 9;
Expand Down Expand Up @@ -75,12 +80,12 @@ var ReactComponentBrowserEnvironment = {
},

/**
* @param {string} markup Markup string to place into the DOM Element.
* @param {ReactDOMMountImage} mountImage Markup to put into the DOM Element.
* @param {DOMElement} container DOM Element to insert markup into.
* @param {boolean} shouldReuseMarkup Should reuse the existing markup in the
* container if possible.
*/
mountImageIntoNode: function(markup, container, shouldReuseMarkup) {
mountImageIntoNode: function(mountImage, container, shouldReuseMarkup) {
invariant(
container && (
container.nodeType === ELEMENT_NODE_TYPE ||
Expand All @@ -90,7 +95,7 @@ var ReactComponentBrowserEnvironment = {
);
if (shouldReuseMarkup) {
if (ReactMarkupChecksum.canReuseMarkup(
markup,
mountImage.markup,
getReactRootElementInContainer(container))) {
return;
} else {
Expand All @@ -112,25 +117,31 @@ var ReactComponentBrowserEnvironment = {
// to mutate documentElement which requires doing some crazy tricks. See
// mutateHTMLNodeWithMarkup()
if (container.nodeType === DOC_NODE_TYPE) {
mutateHTMLNodeWithMarkup(container.documentElement, markup);
mutateHTMLNodeWithMarkup(container.documentElement, mountImage.markup);
return;
}

if (__DEV__) {
validateNodeNesting(container.nodeName, mountImage.nodeName);
}

// Asynchronously inject markup by ensuring that the container is not in
// the document when settings its `innerHTML`.
var parent = container.parentNode;
if (parent) {
var next = container.nextSibling;
parent.removeChild(container);
container.innerHTML = markup;
container.innerHTML = mountImage.markup;
if (next) {
parent.insertBefore(container, next);
} else {
parent.appendChild(container);
}
} else {
container.innerHTML = markup;
container.innerHTML = mountImage.markup;
}

ReactDOMMountImage.release(mountImage);
}
};

Expand Down
21 changes: 16 additions & 5 deletions src/core/ReactCompositeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
var ReactComponent = require('ReactComponent');
var ReactContext = require('ReactContext');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactDOMMountImage = require('ReactDOMMountImage');
var ReactErrorUtils = require('ReactErrorUtils');
var ReactOwner = require('ReactOwner');
var ReactMount = require('ReactMount');
var ReactPerf = require('ReactPerf');
var ReactPropTransferer = require('ReactPropTransferer');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
Expand All @@ -36,6 +38,10 @@ var mixInto = require('mixInto');
var objMap = require('objMap');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');

if (__DEV__) {
var validateNodeNesting = require('validateNodeNesting');
}

/**
* Policies that describe methods in `ReactCompositeComponentInterface`.
*/
Expand Down Expand Up @@ -659,7 +665,7 @@ var ReactCompositeComponentMixin = {
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy
* @return {?string} Rendered markup to be inserted into the DOM.
* @return {ReactDOMMountImage} Mount image to be inserted into the DOM.
* @final
* @internal
*/
Expand Down Expand Up @@ -700,15 +706,15 @@ var ReactCompositeComponentMixin = {

// Done with mounting, `setState` will now trigger UI changes.
this._compositeLifeCycleState = null;
var markup = this._renderedComponent.mountComponent(
var mountImage = this._renderedComponent.mountComponent(
rootID,
transaction,
mountDepth + 1
);
if (this.componentDidMount) {
transaction.getReactMountReady().enqueue(this, this.componentDidMount);
}
return markup;
return mountImage;
}
),

Expand Down Expand Up @@ -1052,15 +1058,20 @@ var ReactCompositeComponentMixin = {
var prevComponentID = prevComponent._rootNodeID;
prevComponent.unmountComponent();
this._renderedComponent = nextComponent;
var nextMarkup = nextComponent.mountComponent(
var nextMountImage = nextComponent.mountComponent(
thisID,
transaction,
this._mountDepth + 1
);
if (__DEV__) {
var parentNode = ReactMount.getNode(prevComponentID).parentNode;
validateNodeNesting(parentNode.nodeName, nextMountImage.nodeName);
}
ReactComponent.DOMIDOperations.dangerouslyReplaceNodeWithMarkupByID(
prevComponentID,
nextMarkup
nextMountImage.markup
);
ReactDOMMountImage.release(nextMountImage);
}
}
),
Expand Down
38 changes: 29 additions & 9 deletions src/core/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var CSSPropertyOperations = require('CSSPropertyOperations');
var DOMProperty = require('DOMProperty');
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactComponent = require('ReactComponent');
var ReactDOMMountImage = require('ReactDOMMountImage');
var ReactEventEmitter = require('ReactEventEmitter');
var ReactMultiChild = require('ReactMultiChild');
var ReactMount = require('ReactMount');
Expand All @@ -34,6 +35,10 @@ var keyOf = require('keyOf');
var merge = require('merge');
var mixInto = require('mixInto');

if (__DEV__) {
var validateNodeNesting = require('validateNodeNesting');
}

var putListener = ReactEventEmitter.putListener;
var deleteListener = ReactEventEmitter.deleteListener;
var registrationNames = ReactEventEmitter.registrationNames;
Expand Down Expand Up @@ -83,7 +88,7 @@ ReactDOMComponent.Mixin = {
* @param {string} rootID The root DOM ID for this node.
* @param {ReactReconcileTransaction} transaction
* @param {number} mountDepth number of components in the owner hierarchy
* @return {string} The computed markup.
* @return {ReactDOMMountImage} The computed mount image.
*/
mountComponent: ReactPerf.measure(
'ReactDOMComponent',
Expand All @@ -96,9 +101,21 @@ ReactDOMComponent.Mixin = {
mountDepth
);
assertValidProps(this.props);
return (
var contentImages = this._createContentMountImages(transaction);
var contentMarkup = contentImages.join('');
for (var i = 0; i < contentImages.length; i++) {
var image = contentImages[i];
if (__DEV__) {
if (image.nodeName != null) {
validateNodeNesting(this.tagName, image.nodeName);
}
}
ReactDOMMountImage.release(image);
}
return ReactDOMMountImage.getPooled(
this.tagName,
this._createOpenTagMarkup() +
this._createContentMarkup(transaction) +
contentMarkup +
this._tagClose
);
}
Expand Down Expand Up @@ -153,30 +170,33 @@ ReactDOMComponent.Mixin = {
*
* @private
* @param {ReactReconcileTransaction} transaction
* @return {string} Content markup.
* @return {array} List of mount images
*/
_createContentMarkup: function(transaction) {
_createContentMountImages: 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;
return [ReactDOMMountImage.getPooled(null, innerHTML.__html)];
}
} else {
var contentToUse =
CONTENT_TYPES[typeof this.props.children] ? this.props.children : null;
var childrenToUse = contentToUse != null ? null : this.props.children;
if (contentToUse != null) {
return escapeTextForBrowser(contentToUse);
return [ReactDOMMountImage.getPooled(
'#text',
escapeTextForBrowser(contentToUse)
)];
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
childrenToUse,
transaction
);
return mountImages.join('');
return mountImages;
}
}
return '';
return [];
},

receiveComponent: function(nextComponent, transaction) {
Expand Down
59 changes: 59 additions & 0 deletions src/core/ReactDOMMountImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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 ReactDOMMountImage
*/

"use strict";

var PooledClass = require('PooledClass');

var mixInto = require('mixInto');

/**
* A class to keep track of strings of markup alongside
*
* This implements `PooledClass`, so you should never need to instantiate this.
* Instead, use `ReactDOMMountImage.getPooled()`.
*
* @param {?string} nodeName Tag name, '#text', or null if unknown
* @param {string} markup HTML to be mounted into the DOM
* @class ReactDOMMountImage
* @implements PooledClass
* @internal
*/
function ReactDOMMountImage(nodeName, markup) {
this.nodeName = nodeName;
this.markup = markup;
}

mixInto(ReactDOMMountImage, {
toString: function() {
// Allow using .join('') on an array of mount images
return this.markup;
},

/**
* `PooledClass` looks for this.
*/
destructor: function() {
this.nodeName = null;
this.markup = null;
}
});

PooledClass.addPoolingTo(ReactDOMMountImage, PooledClass.twoArgumentPooler);

module.exports = ReactDOMMountImage;
15 changes: 8 additions & 7 deletions src/core/ReactMultiChild.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"use strict";

var ReactComponent = require('ReactComponent');
var ReactDOMMountImage = require('ReactDOMMountImage');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');

var flattenChildren = require('flattenChildren');
Expand Down Expand Up @@ -199,7 +200,6 @@ var ReactMultiChild = {
transaction,
this._mountDepth + 1
);
child._mountImage = mountImage;
child._mountIndex = index;
mountImages.push(mountImage);
index++;
Expand Down Expand Up @@ -350,11 +350,12 @@ var ReactMultiChild = {
/**
* Creates a child component.
*
* @param {ReactComponent} child Component to create.
* @param {ReactDOMMountImage} mountImage Markup to insert.
* @param {number} mountIndex Destination index of markup.
* @protected
*/
createChild: function(child) {
enqueueMarkup(this._rootNodeID, child._mountImage, child._mountIndex);
createChild: function(mountImage, mountIndex) {
enqueueMarkup(this._rootNodeID, mountImage.markup, mountIndex);
},

/**
Expand Down Expand Up @@ -396,9 +397,10 @@ var ReactMultiChild = {
transaction,
this._mountDepth + 1
);
child._mountImage = mountImage;
child._mountIndex = index;
this.createChild(child);
// TODO: Use validateNodeNesting to check DOM structure
this.createChild(mountImage, index);
ReactDOMMountImage.release(mountImage);
this._renderedChildren = this._renderedChildren || {};
this._renderedChildren[name] = child;
},
Expand All @@ -415,7 +417,6 @@ var ReactMultiChild = {
_unmountChildByName: function(child, name) {
if (ReactComponent.isValidComponent(child)) {
this.removeChild(child);
child._mountImage = null;
child._mountIndex = null;
child.unmountComponent();
delete this._renderedChildren[name];
Expand Down
Loading