Merge pull request #2923 from sebmarkbage/dropcomponentmixin

Replace ReactComponentMixin with ReactReconciler
This commit is contained in:
Sebastian Markbåge 2015-01-23 18:06:50 -08:00
commit 1c90efbf7c
14 changed files with 192 additions and 306 deletions

View File

@ -16,7 +16,6 @@
var DOMPropertyOperations = require('DOMPropertyOperations'); var DOMPropertyOperations = require('DOMPropertyOperations');
var EventPluginUtils = require('EventPluginUtils'); var EventPluginUtils = require('EventPluginUtils');
var ReactChildren = require('ReactChildren'); var ReactChildren = require('ReactChildren');
var ReactComponent = require('ReactComponent');
var ReactComponentBase = require('ReactComponentBase'); var ReactComponentBase = require('ReactComponentBase');
var ReactClass = require('ReactClass'); var ReactClass = require('ReactClass');
var ReactContext = require('ReactContext'); var ReactContext = require('ReactContext');
@ -86,7 +85,7 @@ if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') { typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
Component: ReactComponent, // TODO: Inject a hook for notifying devtools of updates
CurrentOwner: ReactCurrentOwner, CurrentOwner: ReactCurrentOwner,
DOMComponent: ReactDOMComponent, DOMComponent: ReactDOMComponent,
DOMPropertyOperations: DOMPropertyOperations, DOMPropertyOperations: DOMPropertyOperations,

View File

@ -17,7 +17,6 @@
var CSSPropertyOperations = require('CSSPropertyOperations'); var CSSPropertyOperations = require('CSSPropertyOperations');
var DOMProperty = require('DOMProperty'); var DOMProperty = require('DOMProperty');
var DOMPropertyOperations = require('DOMPropertyOperations'); var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactComponent = require('ReactComponent');
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactMount = require('ReactMount'); var ReactMount = require('ReactMount');
var ReactMultiChild = require('ReactMultiChild'); var ReactMultiChild = require('ReactMultiChild');
@ -162,7 +161,6 @@ function validateDangerousTag(tag) {
* object mapping of style properties to values. * object mapping of style properties to values.
* *
* @constructor ReactDOMComponent * @constructor ReactDOMComponent
* @extends ReactComponent
* @extends ReactMultiChild * @extends ReactMultiChild
*/ */
function ReactDOMComponent(tag) { function ReactDOMComponent(tag) {
@ -177,6 +175,10 @@ ReactDOMComponent.displayName = 'ReactDOMComponent';
ReactDOMComponent.Mixin = { ReactDOMComponent.Mixin = {
construct: function(element) {
this._currentElement = element;
},
/** /**
* Generates root tag markup then recurses. This method has side effects and * Generates root tag markup then recurses. This method has side effects and
* is not idempotent. * is not idempotent.
@ -187,12 +189,6 @@ ReactDOMComponent.Mixin = {
* @return {string} The computed markup. * @return {string} The computed markup.
*/ */
mountComponent: function(rootID, transaction, context) { mountComponent: function(rootID, transaction, context) {
ReactComponent.Mixin.mountComponent.call(
this,
rootID,
transaction,
context
);
this._rootNodeID = rootID; this._rootNodeID = rootID;
assertValidProps(this._currentElement.props); assertValidProps(this._currentElement.props);
var closeTag = omittedCloseTags[this._tag] ? '' : '</' + this._tag + '>'; var closeTag = omittedCloseTags[this._tag] ? '' : '</' + this._tag + '>';
@ -300,18 +296,6 @@ ReactDOMComponent.Mixin = {
}, },
receiveComponent: function(nextElement, transaction, context) { receiveComponent: function(nextElement, transaction, context) {
if (nextElement === this._currentElement &&
nextElement._owner != null) {
// Since elements are immutable after the owner is rendered,
// we can do a cheap identity compare here to determine if this is a
// superfluous reconcile. It's possible for state to be mutable but such
// change should trigger an update of the owner which would recreate
// the element. We explicitly check for the existence of an owner since
// it's possible for an element created outside a composite to be
// deeply mutated and reused.
return;
}
var prevElement = this._currentElement; var prevElement = this._currentElement;
this._currentElement = nextElement; this._currentElement = nextElement;
this.updateComponent(transaction, prevElement, nextElement, context); this.updateComponent(transaction, prevElement, nextElement, context);
@ -329,13 +313,6 @@ ReactDOMComponent.Mixin = {
*/ */
updateComponent: function(transaction, prevElement, nextElement, context) { updateComponent: function(transaction, prevElement, nextElement, context) {
assertValidProps(this._currentElement.props); assertValidProps(this._currentElement.props);
ReactComponent.Mixin.updateComponent.call(
this,
transaction,
prevElement,
nextElement,
context
);
this._updateDOMProperties(prevElement.props, transaction); this._updateDOMProperties(prevElement.props, transaction);
this._updateDOMChildren(prevElement.props, transaction, context); this._updateDOMChildren(prevElement.props, transaction, context);
}, },
@ -498,7 +475,6 @@ ReactDOMComponent.Mixin = {
unmountComponent: function() { unmountComponent: function() {
this.unmountChildren(); this.unmountChildren();
ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID); ReactBrowserEventEmitter.deleteAllListeners(this._rootNodeID);
ReactComponent.Mixin.unmountComponent.call(this);
ReactMount.purgeID(this._rootNodeID); ReactMount.purgeID(this._rootNodeID);
this._rootNodeID = null; this._rootNodeID = null;
} }
@ -512,7 +488,6 @@ ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
assign( assign(
ReactDOMComponent.prototype, ReactDOMComponent.prototype,
ReactComponent.Mixin,
ReactDOMComponent.Mixin, ReactDOMComponent.Mixin,
ReactMultiChild.Mixin ReactMultiChild.Mixin
); );

View File

@ -107,13 +107,6 @@ assign(ReactDOMTextComponent.prototype, {
} }
}, },
updateComponent: function() {
invariant(
false,
'ReactDOMTextComponent: updateComponent() should never be called'
);
},
unmountComponent: function() { unmountComponent: function() {
// TODO: Is this necessary? // TODO: Is this necessary?
ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID); ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID);

View File

@ -21,6 +21,7 @@ var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactInstanceMap = require('ReactInstanceMap'); var ReactInstanceMap = require('ReactInstanceMap');
var ReactMarkupChecksum = require('ReactMarkupChecksum'); var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactPerf = require('ReactPerf'); var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdates = require('ReactUpdates'); var ReactUpdates = require('ReactUpdates');
var emptyObject = require('emptyObject'); var emptyObject = require('emptyObject');
@ -241,7 +242,9 @@ function mountComponentIntoNode(
container, container,
transaction, transaction,
shouldReuseMarkup) { shouldReuseMarkup) {
var markup = this.mountComponent(rootID, transaction, emptyObject); var markup = ReactReconciler.mountComponent(
this, rootID, transaction, emptyObject
);
this._isTopLevel = true; this._isTopLevel = true;
ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup); ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup);
} }
@ -553,7 +556,7 @@ var ReactMount = {
* @see {ReactMount.unmountComponentAtNode} * @see {ReactMount.unmountComponentAtNode}
*/ */
unmountComponentFromNode: function(instance, container) { unmountComponentFromNode: function(instance, container) {
instance.unmountComponent(); ReactReconciler.unmountComponent(instance);
if (container.nodeType === DOC_NODE_TYPE) { if (container.nodeType === DOC_NODE_TYPE) {
container = container.documentElement; container = container.documentElement;

View File

@ -308,15 +308,13 @@ describe('ReactDOMComponent', function() {
beforeEach(function() { beforeEach(function() {
require('mock-modules').dumpCache(); require('mock-modules').dumpCache();
var ReactComponent = require('ReactComponent');
var ReactMultiChild = require('ReactMultiChild'); var ReactMultiChild = require('ReactMultiChild');
var ReactDOMComponent = require('ReactDOMComponent'); var ReactDOMComponent = require('ReactDOMComponent');
var ReactReconcileTransaction = require('ReactReconcileTransaction'); var ReactReconcileTransaction = require('ReactReconcileTransaction');
var StubNativeComponent = function(element) { var StubNativeComponent = function(element) {
ReactComponent.Mixin.construct.call(this, element); this._currentElement = element;
}; };
assign(StubNativeComponent.prototype, ReactComponent.Mixin);
assign(StubNativeComponent.prototype, ReactDOMComponent.Mixin); assign(StubNativeComponent.prototype, ReactDOMComponent.Mixin);
assign(StubNativeComponent.prototype, ReactMultiChild.Mixin); assign(StubNativeComponent.prototype, ReactMultiChild.Mixin);

View File

@ -12,6 +12,8 @@
'use strict'; 'use strict';
var ReactReconciler = require('ReactReconciler');
var flattenChildren = require('flattenChildren'); var flattenChildren = require('flattenChildren');
var instantiateReactComponent = require('instantiateReactComponent'); var instantiateReactComponent = require('instantiateReactComponent');
var shouldUpdateReactComponent = require('shouldUpdateReactComponent'); var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
@ -78,11 +80,13 @@ var ReactChildReconciler = {
var prevElement = prevChild && prevChild._currentElement; var prevElement = prevChild && prevChild._currentElement;
var nextElement = nextChildren[name]; var nextElement = nextChildren[name];
if (shouldUpdateReactComponent(prevElement, nextElement)) { if (shouldUpdateReactComponent(prevElement, nextElement)) {
prevChild.receiveComponent(nextElement, transaction, context); ReactReconciler.receiveComponent(
prevChild, nextElement, transaction, context
);
nextChildren[name] = prevChild; nextChildren[name] = prevChild;
} else { } else {
if (prevChild) { if (prevChild) {
prevChild.unmountComponent(); ReactReconciler.unmountComponent(prevChild, name);
} }
// The child must be instantiated before it's mounted. // The child must be instantiated before it's mounted.
var nextChildInstance = instantiateReactComponent( var nextChildInstance = instantiateReactComponent(
@ -96,7 +100,7 @@ var ReactChildReconciler = {
for (name in prevChildren) { for (name in prevChildren) {
if (prevChildren.hasOwnProperty(name) && if (prevChildren.hasOwnProperty(name) &&
!(nextChildren && nextChildren.hasOwnProperty(name))) { !(nextChildren && nextChildren.hasOwnProperty(name))) {
prevChildren[name].unmountComponent(); ReactReconciler.unmountComponent(prevChildren[name]);
} }
} }
return nextChildren; return nextChildren;
@ -112,7 +116,7 @@ var ReactChildReconciler = {
unmountChildren: function(renderedChildren) { unmountChildren: function(renderedChildren) {
for (var name in renderedChildren) { for (var name in renderedChildren) {
var renderedChild = renderedChildren[name]; var renderedChild = renderedChildren[name];
renderedChild.unmountComponent(); ReactReconciler.unmountComponent(renderedChild);
} }
} }

View File

@ -1,147 +0,0 @@
/**
* Copyright 2013-2014, 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 ReactComponent
*/
'use strict';
var ReactElementValidator = require('ReactElementValidator');
var invariant = require('invariant');
/**
* Components are the basic units of composition in React.
*
* Every component accepts a set of keyed input parameters known as "props" that
* are initialized by the constructor. Once a component is mounted, the props
* can be mutated using `setProps` or `replaceProps`.
*
* Every component is capable of the following operations:
*
* `mountComponent`
* Initializes the component, renders markup, and registers event listeners.
*
* `receiveComponent`
* Updates the rendered DOM nodes to match the given component.
*
* `unmountComponent`
* Releases any resources allocated by this component.
*
* Components can also be "owned" by other components. Being owned by another
* component means being constructed by that component. This is different from
* being the child of a component, which means having a DOM representation that
* is a child of the DOM representation of that component.
*
* @class ReactComponent
*/
var ReactComponent = {
/**
* Injected module that provides ability to mutate individual properties.
* Injected into the base class because many different subclasses need access
* to this.
*
* @internal
*/
BackendIDOperations: null,
/**
* Base functionality for every ReactComponent constructor. Mixed into the
* `ReactComponent` prototype, but exposed statically for easy access.
*
* @lends {ReactComponent.prototype}
*/
Mixin: {
/**
* Base constructor for all React components.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.construct.call(this, ...)`.
*
* @param {ReactElement} element
* @internal
*/
construct: function(element) {
// We keep the old element and a reference to the pending element
// to track updates.
this._currentElement = element;
// These two fields are used by the DOM and ART diffing algorithms
// respectively. Instead of using expandos on components, we should be
// storing the state needed by the diffing algorithms elsewhere.
this._mountIndex = 0;
this._mountImage = null;
},
/**
* Initializes the component, renders markup, and registers event listeners.
*
* NOTE: This does not insert any nodes into the DOM.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.mountComponent.call(this, ...)`.
*
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @return {?string} Rendered markup to be inserted into the DOM.
* @internal
*/
mountComponent: function(rootID, transaction, context) {
if (__DEV__) {
ReactElementValidator.checkAndWarnForMutatedProps(this._currentElement);
}
// Effectively: return '';
},
/**
* Releases any resources allocated by `mountComponent`.
*
* NOTE: This does not remove any nodes from the DOM.
*
* Subclasses that override this method should make sure to invoke
* `ReactComponent.Mixin.unmountComponent.call(this)`.
*
* @internal
*/
unmountComponent: function() {
},
/**
* Updates the component's currently mounted representation.
*
* @param {ReactReconcileTransaction} transaction
* @param {object} prevElement
* @param {object} nextElement
* @internal
*/
updateComponent: function(transaction, prevElement, nextElement, context) {
if (__DEV__) {
ReactElementValidator.checkAndWarnForMutatedProps(nextElement);
}
},
/**
* Get the publicly accessible representation of this component - i.e. what
* is exposed by refs and returned by React.render. Can be null for
* stateless components.
*
* @return {?ReactComponent} the actual sibling Component.
* @internal
*/
getPublicInstance: function() {
invariant(
false,
'getPublicInstance should never be called on the base class. It must ' +
'be overridden.'
);
}
}
};
module.exports = ReactComponent;

View File

@ -11,17 +11,17 @@
'use strict'; 'use strict';
var ReactComponent = require('ReactComponent');
var ReactComponentEnvironment = require('ReactComponentEnvironment'); var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactContext = require('ReactContext'); var ReactContext = require('ReactContext');
var ReactCurrentOwner = require('ReactCurrentOwner'); var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactElement = require('ReactElement'); var ReactElement = require('ReactElement');
var ReactElementValidator = require('ReactElementValidator');
var ReactInstanceMap = require('ReactInstanceMap'); var ReactInstanceMap = require('ReactInstanceMap');
var ReactNativeComponent = require('ReactNativeComponent'); var ReactNativeComponent = require('ReactNativeComponent');
var ReactPerf = require('ReactPerf'); var ReactPerf = require('ReactPerf');
var ReactPropTypeLocations = require('ReactPropTypeLocations'); var ReactPropTypeLocations = require('ReactPropTypeLocations');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames'); var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactRef = require('ReactRef'); var ReactReconciler = require('ReactReconciler');
var ReactUpdates = require('ReactUpdates'); var ReactUpdates = require('ReactUpdates');
var assign = require('Object.assign'); var assign = require('Object.assign');
@ -97,8 +97,7 @@ var nextMountID = 1;
/** /**
* @lends {ReactCompositeComponent.prototype} * @lends {ReactCompositeComponent.prototype}
*/ */
var ReactCompositeComponentMixin = assign({}, var ReactCompositeComponentMixin = {
ReactComponent.Mixin, {
/** /**
* Base constructor for all composite component. * Base constructor for all composite component.
@ -108,6 +107,8 @@ var ReactCompositeComponentMixin = assign({},
* @internal * @internal
*/ */
construct: function(element) { construct: function(element) {
this._currentElement = element;
this._rootNodeID = null; this._rootNodeID = null;
this._instance = null; this._instance = null;
@ -119,9 +120,6 @@ var ReactCompositeComponentMixin = assign({},
this._renderedComponent = null; this._renderedComponent = null;
// Children can be either an array or more than one argument
ReactComponent.Mixin.construct.apply(this, arguments);
this._context = null; this._context = null;
this._mountOrder = 0; this._mountOrder = 0;
this._isTopLevel = false; this._isTopLevel = false;
@ -150,13 +148,6 @@ var ReactCompositeComponentMixin = assign({},
* @internal * @internal
*/ */
mountComponent: function(rootID, transaction, context) { mountComponent: function(rootID, transaction, context) {
ReactComponent.Mixin.mountComponent.call(
this,
rootID,
transaction,
context
);
this._context = context; this._context = context;
this._mountOrder = nextMountID++; this._mountOrder = nextMountID++;
this._rootNodeID = rootID; this._rootNodeID = rootID;
@ -253,7 +244,8 @@ var ReactCompositeComponentMixin = assign({},
// Done with mounting, `setState` will now trigger UI changes. // Done with mounting, `setState` will now trigger UI changes.
this._compositeLifeCycleState = null; this._compositeLifeCycleState = null;
var markup = this._renderedComponent.mountComponent( var markup = ReactReconciler.mountComponent(
this._renderedComponent,
rootID, rootID,
transaction, transaction,
this._processChildContext(context) this._processChildContext(context)
@ -261,16 +253,8 @@ var ReactCompositeComponentMixin = assign({},
if (inst.componentDidMount) { if (inst.componentDidMount) {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
} }
transaction.getReactMountReady().enqueue(this.attachRefs, this);
return markup;
},
/** return markup;
* Helper to call ReactRef.attachRefs with this composite component, split out
* to avoid allocations in the transaction mount-ready queue.
*/
attachRefs: function() {
ReactRef.attachRefs(this, this._currentElement);
}, },
/** /**
@ -282,15 +266,13 @@ var ReactCompositeComponentMixin = assign({},
unmountComponent: function() { unmountComponent: function() {
var inst = this._instance; var inst = this._instance;
ReactRef.detachRefs(this, this._currentElement);
this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING; this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;
if (inst.componentWillUnmount) { if (inst.componentWillUnmount) {
inst.componentWillUnmount(); inst.componentWillUnmount();
} }
this._compositeLifeCycleState = null; this._compositeLifeCycleState = null;
this._renderedComponent.unmountComponent(); ReactReconciler.unmountComponent(this._renderedComponent);
this._renderedComponent = null; this._renderedComponent = null;
// Reset pending fields // Reset pending fields
@ -299,8 +281,6 @@ var ReactCompositeComponentMixin = assign({},
this._pendingCallbacks = null; this._pendingCallbacks = null;
this._pendingElement = null; this._pendingElement = null;
ReactComponent.Mixin.unmountComponent.call(this);
ReactComponentEnvironment.unmountIDFromEnvironment(this._rootNodeID); ReactComponentEnvironment.unmountIDFromEnvironment(this._rootNodeID);
this._context = null; this._context = null;
@ -636,22 +616,28 @@ var ReactCompositeComponentMixin = assign({},
} }
}, },
receiveComponent: function(nextElement, transaction, context) { receiveComponent: function(nextElement, transaction, nextContext) {
if (nextElement === this._currentElement && var compositeLifeCycleState = this._compositeLifeCycleState;
nextElement._owner != null) { // Do not trigger a state transition if we are in the middle of mounting or
// Since elements are immutable after the owner is rendered, // receiving props because both of those will already be doing this.
// we can do a cheap identity compare here to determine if this is a if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING ||
// superfluous reconcile. It's possible for state to be mutable but such compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) {
// change should trigger an update of the owner which would recreate
// the element. We explicitly check for the existence of an owner since
// it's possible for an element created outside a composite to be
// deeply mutated and reused.
return; return;
} }
this._pendingElement = nextElement; var prevElement = this._currentElement;
this._pendingContext = context; var prevContext = this._context;
this.performUpdateIfNecessary(transaction);
this._pendingElement = null;
this._pendingContext = null;
this.updateComponent(
transaction,
prevElement,
nextElement,
prevContext,
nextContext
);
}, },
/** /**
@ -662,42 +648,30 @@ var ReactCompositeComponentMixin = assign({},
* @internal * @internal
*/ */
performUpdateIfNecessary: function(transaction) { performUpdateIfNecessary: function(transaction) {
var compositeLifeCycleState = this._compositeLifeCycleState; if (this._pendingElement != null || this._pendingContext != null) {
// Do not trigger a state transition if we are in the middle of mounting or ReactReconciler.receiveComponent(
// receiving props because both of those will already be doing this. this,
if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING || this._pendingElement || this._currentElement,
compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) { transaction,
return; this._pendingContext || this._context
);
} }
if (this._pendingElement == null && if (this._pendingState != null || this._pendingForceUpdate) {
this._pendingState == null && if (__DEV__) {
this._pendingContext == null && ReactElementValidator.checkAndWarnForMutatedProps(
!this._pendingForceUpdate) { this._currentElement
return; );
} }
var prevElement = this._currentElement; this.updateComponent(
var nextElement = prevElement; transaction,
if (this._pendingElement != null) { this._currentElement,
nextElement = this._pendingElement; this._currentElement,
this._pendingElement = null; this._context,
this._context
);
} }
var prevContext = this._context;
var nextContext = prevContext;
if (this._pendingContext != null) {
nextContext = this._pendingContext;
this._pendingContext = null;
}
this.updateComponent(
transaction,
prevElement,
nextElement,
prevContext,
nextContext
);
}, },
/** /**
@ -746,15 +720,6 @@ var ReactCompositeComponentMixin = assign({},
prevUnmaskedContext, prevUnmaskedContext,
nextUnmaskedContext nextUnmaskedContext
) { ) {
ReactComponent.Mixin.updateComponent.call(
this,
transaction,
prevParentElement,
nextParentElement,
prevUnmaskedContext,
nextUnmaskedContext
);
var inst = this._instance; var inst = this._instance;
var prevContext = inst.context; var prevContext = inst.context;
@ -762,16 +727,6 @@ var ReactCompositeComponentMixin = assign({},
var nextContext = prevContext; var nextContext = prevContext;
var nextProps = prevProps; var nextProps = prevProps;
var refsChanged = ReactRef.shouldUpdateRefs(
this,
prevParentElement,
nextParentElement
);
if (refsChanged) {
ReactRef.detachRefs(this, prevParentElement);
}
// Distinguish between a props update versus a simple state update // Distinguish between a props update versus a simple state update
if (prevParentElement !== nextParentElement) { if (prevParentElement !== nextParentElement) {
nextContext = this._processContext(nextParentElement._context); nextContext = this._processContext(nextParentElement._context);
@ -829,11 +784,6 @@ var ReactCompositeComponentMixin = assign({},
inst.state = nextState; inst.state = nextState;
inst.context = nextContext; inst.context = nextContext;
} }
// Update refs regardless of what shouldComponentUpdate returns
if (refsChanged) {
transaction.getReactMountReady().enqueue(this.attachRefs, this);
}
}, },
/** /**
@ -894,7 +844,8 @@ var ReactCompositeComponentMixin = assign({},
var prevRenderedElement = prevComponentInstance._currentElement; var prevRenderedElement = prevComponentInstance._currentElement;
var nextRenderedElement = this._renderValidatedComponent(); var nextRenderedElement = this._renderValidatedComponent();
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
prevComponentInstance.receiveComponent( ReactReconciler.receiveComponent(
prevComponentInstance,
nextRenderedElement, nextRenderedElement,
transaction, transaction,
this._processChildContext(context) this._processChildContext(context)
@ -903,13 +854,14 @@ var ReactCompositeComponentMixin = assign({},
// These two IDs are actually the same! But nothing should rely on that. // These two IDs are actually the same! But nothing should rely on that.
var thisID = this._rootNodeID; var thisID = this._rootNodeID;
var prevComponentID = prevComponentInstance._rootNodeID; var prevComponentID = prevComponentInstance._rootNodeID;
prevComponentInstance.unmountComponent(); ReactReconciler.unmountComponent(prevComponentInstance);
this._renderedComponent = this._instantiateReactComponent( this._renderedComponent = this._instantiateReactComponent(
nextRenderedElement, nextRenderedElement,
this._currentElement.type this._currentElement.type
); );
var nextMarkup = this._renderedComponent.mountComponent( var nextMarkup = ReactReconciler.mountComponent(
this._renderedComponent,
thisID, thisID,
transaction, transaction,
context context
@ -957,7 +909,6 @@ var ReactCompositeComponentMixin = assign({},
this._currentElement._context this._currentElement._context
); );
ReactCurrentOwner.current = this; ReactCurrentOwner.current = this;
var inst = this._instance;
try { try {
renderedComponent = renderedComponent =
this._renderValidatedComponentWithoutOwnerOrContext(); this._renderValidatedComponentWithoutOwnerOrContext();
@ -1033,7 +984,7 @@ var ReactCompositeComponentMixin = assign({},
// Stub // Stub
_instantiateReactComponent: null _instantiateReactComponent: null
}); };
ReactPerf.measureMethods( ReactPerf.measureMethods(
ReactCompositeComponentMixin, ReactCompositeComponentMixin,

View File

@ -15,6 +15,7 @@
var ReactComponentEnvironment = require('ReactComponentEnvironment'); var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes'); var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var ReactReconciler = require('ReactReconciler');
var ReactChildReconciler = require('ReactChildReconciler'); var ReactChildReconciler = require('ReactChildReconciler');
/** /**
@ -184,11 +185,12 @@ var ReactMultiChild = {
var mountImages = []; var mountImages = [];
var index = 0; var index = 0;
for (var name in children) { for (var name in children) {
var child = children[name];
if (children.hasOwnProperty(name)) { if (children.hasOwnProperty(name)) {
var child = children[name];
// Inlined for performance, see `ReactInstanceHandles.createReactID`. // Inlined for performance, see `ReactInstanceHandles.createReactID`.
var rootID = this._rootNodeID + name; var rootID = this._rootNodeID + name;
var mountImage = child.mountComponent( var mountImage = ReactReconciler.mountComponent(
child,
rootID, rootID,
transaction, transaction,
context context
@ -395,7 +397,8 @@ var ReactMultiChild = {
context) { context) {
// Inlined for performance, see `ReactInstanceHandles.createReactID`. // Inlined for performance, see `ReactInstanceHandles.createReactID`.
var rootID = this._rootNodeID + name; var rootID = this._rootNodeID + name;
var mountImage = child.mountComponent( var mountImage = ReactReconciler.mountComponent(
child,
rootID, rootID,
transaction, transaction,
context context

107
src/core/ReactReconciler.js Normal file
View File

@ -0,0 +1,107 @@
/**
* Copyright 2013-2014, 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 ReactReconciler
*/
'use strict';
var ReactRef = require('ReactRef');
var ReactElementValidator = require('ReactElementValidator');
/**
* Helper to call ReactRef.attachRefs with this composite component, split out
* to avoid allocations in the transaction mount-ready queue.
*/
function attachRefs() {
ReactRef.attachRefs(this, this._currentElement);
}
var ReactReconciler = {
/**
* Initializes the component, renders markup, and registers event listeners.
*
* @param {ReactComponent} internalInstance
* @param {string} rootID DOM ID of the root node.
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
* @return {?string} Rendered markup to be inserted into the DOM.
* @final
* @internal
*/
mountComponent: function(internalInstance, rootID, transaction, context) {
var markup = internalInstance.mountComponent(rootID, transaction, context);
if (__DEV__) {
ReactElementValidator.checkAndWarnForMutatedProps(
internalInstance._currentElement
);
}
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
return markup;
},
/**
* Releases any resources allocated by `mountComponent`.
*
* @final
* @internal
*/
unmountComponent: function(internalInstance) {
ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
internalInstance.unmountComponent();
},
/**
* Update a component using a new element.
*
* @param {ReactComponent} internalInstance
* @param {ReactElement} nextElement
* @param {ReactReconcileTransaction} transaction
* @param {object} context
* @internal
*/
receiveComponent: function(
internalInstance, nextElement, transaction, context
) {
var prevElement = internalInstance._currentElement;
if (nextElement === prevElement && nextElement._owner != null) {
// Since elements are immutable after the owner is rendered,
// we can do a cheap identity compare here to determine if this is a
// superfluous reconcile. It's possible for state to be mutable but such
// change should trigger an update of the owner which would recreate
// the element. We explicitly check for the existence of an owner since
// it's possible for an element created outside a composite to be
// deeply mutated and reused.
return;
}
if (__DEV__) {
ReactElementValidator.checkAndWarnForMutatedProps(nextElement);
}
var refsChanged = ReactRef.shouldUpdateRefs(
this,
prevElement,
nextElement
);
if (refsChanged) {
ReactRef.detachRefs(internalInstance, prevElement);
}
internalInstance.receiveComponent(nextElement, transaction, context);
if (refsChanged) {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
}
};
module.exports = ReactReconciler;

View File

@ -15,7 +15,6 @@ var keyMirror = require('keyMirror');
var React; var React;
var ReactTestUtils; var ReactTestUtils;
var ReactComponent;
var ReactCompositeComponent; var ReactCompositeComponent;
var ReactInstanceMap; var ReactInstanceMap;
var CompositeComponentLifeCycle; var CompositeComponentLifeCycle;
@ -97,7 +96,6 @@ describe('ReactComponentLifeCycle', function() {
require('mock-modules').dumpCache(); require('mock-modules').dumpCache();
React = require('React'); React = require('React');
ReactTestUtils = require('ReactTestUtils'); ReactTestUtils = require('ReactTestUtils');
ReactComponent = require('ReactComponent');
ReactCompositeComponent = require('ReactCompositeComponent'); ReactCompositeComponent = require('ReactCompositeComponent');
CompositeComponentLifeCycle = ReactCompositeComponent.LifeCycle; CompositeComponentLifeCycle = ReactCompositeComponent.LifeCycle;

View File

@ -14,7 +14,6 @@
var ChildUpdates; var ChildUpdates;
var MorphingComponent; var MorphingComponent;
var React; var React;
var ReactComponent;
var ReactCurrentOwner; var ReactCurrentOwner;
var ReactDoNotBindDeprecated; var ReactDoNotBindDeprecated;
var ReactMount; var ReactMount;
@ -36,7 +35,6 @@ describe('ReactCompositeComponent', function() {
reactComponentExpect = require('reactComponentExpect'); reactComponentExpect = require('reactComponentExpect');
React = require('React'); React = require('React');
ReactComponent = require('ReactComponent');
ReactCurrentOwner = require('ReactCurrentOwner'); ReactCurrentOwner = require('ReactCurrentOwner');
ReactDoNotBindDeprecated = require('ReactDoNotBindDeprecated'); ReactDoNotBindDeprecated = require('ReactDoNotBindDeprecated');
ReactPropTypes = require('ReactPropTypes'); ReactPropTypes = require('ReactPropTypes');

View File

@ -12,7 +12,6 @@
'use strict'; 'use strict';
var React; var React;
var ReactComponent;
var ReactTestUtils; var ReactTestUtils;
var mocks; var mocks;
@ -27,7 +26,6 @@ describe('ReactMockedComponent', function() {
mocks = require('mocks'); mocks = require('mocks');
React = require('React'); React = require('React');
ReactComponent = require('ReactComponent');
ReactTestUtils = require('ReactTestUtils'); ReactTestUtils = require('ReactTestUtils');
OriginalComponent = React.createClass({ OriginalComponent = React.createClass({

View File

@ -108,6 +108,12 @@ function instantiateReactComponent(node, parentCompositeType) {
// Sets up the instance. This can probably just move into the constructor now. // Sets up the instance. This can probably just move into the constructor now.
instance.construct(node); instance.construct(node);
// These two fields are used by the DOM and ART diffing algorithms
// respectively. Instead of using expandos on components, we should be
// storing the state needed by the diffing algorithms elsewhere.
instance._mountIndex = 0;
instance._mountImage = null;
// Internal instances should fully constructed at this point, so they should // Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point. // not get any new fields added to them at this point.
if (__DEV__) { if (__DEV__) {