ReactDOMComponentTree

New module! With this, we have a new module that uses the component tree to store DOM nodes. Most of the logic in this file relates to markup adoption -- if we were to drop that (along with server rendering) this file could just be a `return inst._nativeNode;`.

This works with useCreateElement only because we need to reference each node as it's created.

Events is now the only thing using ReactMount.getNode -- I'll introduce pointers back from the DOM nodes (and a `ReactDOMComponentTree.getInstanceFromNode`) and make that work.
This commit is contained in:
Ben Alpert committed 2015-11-03 17:03:40 -08:00
1 parent 5a7c273a1b
commit ce528457a4
15 files changed
+289 -137

No files matched your search

@@ -0,0 +1,122 @@
/**
* Copyright 2013-2015, 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 ReactDOMComponentTree
*/
'use strict';
var DOMProperty = require('DOMProperty');
var ReactDOMComponentFlags = require('ReactDOMComponentFlags');
var invariant = require('invariant');
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
var Flags = ReactDOMComponentFlags;
/**
* Drill down (through composites and empty components) until we get a native or
* native text component.
*
* This is pretty polymorphic but unavoidable with the current structure we have
* for `_renderedChildren`.
*/
function getRenderedNativeOrTextFromComponent(component) {
var rendered;
while ((rendered = component._renderedComponent)) {
component = rendered;
}
return component;
}
/**
* Populate `_nativeNode` on the rendered native/text component with the given
* DOM node. The passed `inst` can be a composite.
*/
function precacheNode(inst, node) {
var nativeInst = getRenderedNativeOrTextFromComponent(inst);
nativeInst._nativeNode = node;
}
/**
* Populate `_nativeNode` on each child of `inst`, assuming that the children
* match up with the DOM (element) children of `node`.
*
* We cache entire levels at once to avoid an n^2 problem where we access the
* children of a node sequentially and have to walk from the start to our target
* node every time.
*
* Since we update `_renderedChildren` and the actual DOM at (slightly)
* different times, we could race here and not get the
*/
function precacheChildNodes(inst, node) {
if (inst._flags & Flags.hasCachedChildNodes) {
return;
}
var children = inst._renderedChildren;
var childNode = node.firstChild;
outer: for (var name in children) {
if (!children.hasOwnProperty(name)) {
continue;
}
var childInst = children[name];
var childID = childInst._rootNodeID;
if (childID == null) {
// We're currently unmounting this child in ReactMultiChild; skip it.
continue;
}
// We assume the child nodes are in the same order as the child instances.
for (; childNode !== null; childNode = childNode.nextSibling) {
if (childNode.nodeType === 1 &&
childNode.getAttribute(ATTR_NAME) === childID) {
precacheNode(childInst, childNode);
continue outer;
}
}
// We reached the end of the DOM children without finding an ID match.
invariant(false, 'Unable to find element with ID %s.', childID);
}
inst._flags |= Flags.hasCachedChildNodes;
}
/**
* Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding
* DOM node.
*/
function getNodeFromInstance(inst) {
if (inst._nativeNode) {
return inst._nativeNode;
}
// Walk up the tree until we find an ancestor whose DOM node we have cached.
var parents = [];
while (!inst._nativeNode) {
parents.push(inst);
invariant(
inst._nativeParent,
'React DOM tree root should always have a node reference.'
);
inst = inst._nativeParent;
}
// Now parents contains each ancestor that does *not* have a cached native
// node, and `inst` is the deepest ancestor that does.
for (; parents.length; inst = parents.pop()) {
precacheChildNodes(inst, inst._nativeNode);
}
return inst._nativeNode;
}
var ReactDOMComponentTree = {
getNodeFromInstance: getNodeFromInstance,
precacheChildNodes: precacheChildNodes,
precacheNode: precacheNode,
};
module.exports = ReactDOMComponentTree;
@@ -13,56 +13,14 @@
'use strict';
var DOMChildrenOperations = require('DOMChildrenOperations');
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactMount = require('ReactMount');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactPerf = require('ReactPerf');
var invariant = require('invariant');
/**
* Errors for properties that should not be updated with `updatePropertyByID()`.
*
* @type {object}
* @private
*/
var INVALID_PROPERTY_ERRORS = {
dangerouslySetInnerHTML:
'`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.',
style: '`style` must be set using `updateStylesByID()`.',
};
/**
* Operations used to process updates to DOM nodes.
*/
var ReactDOMIDOperations = {
/**
* Updates a DOM node with new property values. This should only be used to
* update DOM properties in `DOMProperty`.
*
* @param {string} id ID of the node to update.
* @param {string} name A valid property name, see `DOMProperty`.
* @param {*} value New value of the property.
* @internal
*/
updatePropertyByID: function(id, name, value) {
var node = ReactMount.getNode(id);
invariant(
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
'updatePropertyByID(...): %s',
INVALID_PROPERTY_ERRORS[name]
);
// If we're updating to null or undefined, we should remove the property
// from the DOM node instead of inadvertantly setting to a string. This
// brings us in line with the same behavior we have on initial render.
if (value != null) {
DOMPropertyOperations.setValueForProperty(node, name, value);
} else {
DOMPropertyOperations.deleteValueForProperty(node, name);
}
},
/**
* Updates a component's children by processing a series of updates.
*
@@ -72,7 +30,9 @@ var ReactDOMIDOperations = {
*/
dangerouslyProcessChildrenUpdates: function(updates, markup) {
for (var i = 0; i < updates.length; i++) {
updates[i].parentNode = ReactMount.getNode(updates[i].parentID);
var update = updates[i];
var node = ReactDOMComponentTree.getNodeFromInstance(update.parentInst);
update.parentNode = node;
}
DOMChildrenOperations.processUpdates(updates, markup);
},
+5
View File
@@ -16,6 +16,7 @@ var DOMLazyTree = require('DOMLazyTree');
var DOMProperty = require('DOMProperty');
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactElement = require('ReactElement');
@@ -267,6 +268,7 @@ function mountComponentIntoNode(
ReactMount._mountImageIntoNode(
markup,
container,
componentInstance,
shouldReuseMarkup,
transaction
);
@@ -951,6 +953,7 @@ var ReactMount = {
_mountImageIntoNode: function(
markup,
container,
instance,
shouldReuseMarkup,
transaction
) {
@@ -966,6 +969,7 @@ var ReactMount = {
if (shouldReuseMarkup) {
var rootElement = getReactRootElementInContainer(container);
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
ReactDOMComponentTree.precacheNode(instance, rootElement);
return;
} else {
var checksum = rootElement.getAttribute(
@@ -1049,6 +1053,7 @@ var ReactMount = {
DOMLazyTree.insertTreeBefore(container, markup, null);
} else {
setInnerHTML(container, markup);
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
}
},
@@ -0,0 +1,62 @@
/**
* Copyright 2015, 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.
*
* @emails react-core
*/
'use strict';
describe('ReactDOMComponentTree', function() {
var React;
var ReactDOM;
var ReactDOMServer;
function renderMarkupIntoDocument(elt) {
var container = document.createElement('div');
// Force server-rendering path:
container.innerHTML = ReactDOMServer.renderToString(elt);
return ReactDOM.render(elt, container);
}
beforeEach(function() {
React = require('React');
ReactDOM = require('ReactDOM');
ReactDOMServer = require('ReactDOMServer');
});
it('finds nodes for instances', function() {
// This is a little hard to test directly. But refs rely on it -- so we
// check that we can find a ref at arbitrary points in the tree, even if
// other nodes don't have a ref.
var Component = React.createClass({
render: function() {
var toRef = this.props.toRef;
return (
<div ref={toRef === 'div' ? 'target' : null}>
<h1 ref={toRef === 'h1' ? 'target' : null}>hello</h1>
<p ref={toRef === 'p' ? 'target' : null}>
<input ref={toRef === 'input' ? 'target' : null} />
</p>
goodbye.
</div>
);
},
});
function renderAndGetRef(toRef) {
var inst = renderMarkupIntoDocument(<Component toRef={toRef} />);
return inst.refs.target.nodeName;
}
expect(renderAndGetRef('div')).toBe('DIV');
expect(renderAndGetRef('h1')).toBe('H1');
expect(renderAndGetRef('p')).toBe('P');
expect(renderAndGetRef('input')).toBe('INPUT');
});
});
@@ -12,42 +12,16 @@
'use strict';
describe('ReactDOMIDOperations', function() {
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactDOMIDOperations = require('ReactDOMIDOperations');
var ReactMount = require('ReactMount');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var keyOf = require('keyOf');
it('should disallow updating special properties', function() {
spyOn(ReactMount, 'getNode');
spyOn(DOMPropertyOperations, 'setValueForProperty');
expect(function() {
ReactDOMIDOperations.updatePropertyByID(
'testID',
keyOf({dangerouslySetInnerHTML: null}),
{__html: 'testContent'}
);
}).toThrow();
expect(
ReactMount.getNode.argsForCall[0][0]
).toBe('testID');
expect(
DOMPropertyOperations.setValueForProperty.calls.length
).toBe(0);
});
it('should update innerHTML and preserve whitespace', function() {
var stubNode = document.createElement('div');
spyOn(ReactMount, 'getNode').andReturn(stubNode);
var html = '\n \t <span> \n testContent \t </span> \n \t';
ReactDOMIDOperations.dangerouslyProcessChildrenUpdates(
[{
parentID: 'testID',
parentInst: {_nativeNode: stubNode},
parentNode: null,
type: ReactMultiChildUpdateTypes.SET_MARKUP,
markupIndex: null,
@@ -58,10 +32,6 @@ describe('ReactDOMIDOperations', function() {
[]
);
expect(
ReactMount.getNode.argsForCall[0][0]
).toBe('testID');
expect(stubNode.innerHTML).toBe(html);
});
});
+2 -2
View File
@@ -13,7 +13,7 @@
'use strict';
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactDOMComponent = require('ReactDOMComponent');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactNodeTypes = require('ReactNodeTypes');
@@ -66,7 +66,7 @@ function findDOMNode(componentOrElement) {
var inst = ReactInstanceMap.get(componentOrElement);
if (inst) {
inst = getNativeComponentFromComposite(inst);
return inst ? ReactDOMComponent.getNodeFromInstance(inst) : null;
return inst ? ReactDOMComponentTree.getNodeFromInstance(inst) : null;
}
if (typeof componentOrElement.render === 'function') {
@@ -12,13 +12,13 @@
'use strict';
var ReactMount = require('ReactMount');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var focusNode = require('focusNode');
var AutoFocusUtils = {
focusDOMComponent: function() {
focusNode(ReactMount.getNode(this._rootNodeID));
focusNode(ReactDOMComponentTree.getNodeFromInstance(this));
},
};
@@ -11,8 +11,9 @@
'use strict';
var ReactDOMIDOperations = require('ReactDOMIDOperations');
var DOMPropertyOperations = require('DOMPropertyOperations');
var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactMount = require('ReactMount');
var ReactUpdates = require('ReactUpdates');
@@ -131,8 +132,8 @@ var ReactDOMInput = {
// TODO: Shouldn't this be getChecked(props)?
var checked = props.checked;
if (checked != null) {
ReactDOMIDOperations.updatePropertyByID(
inst._rootNodeID,
DOMPropertyOperations.setValueForProperty(
ReactDOMComponentTree.getNodeFromInstance(inst),
'checked',
checked || false
);
@@ -142,8 +143,8 @@ var ReactDOMInput = {
if (value != null) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
ReactDOMIDOperations.updatePropertyByID(
inst._rootNodeID,
DOMPropertyOperations.setValueForProperty(
ReactDOMComponentTree.getNodeFromInstance(inst),
'value',
'' + value
);
@@ -163,7 +164,7 @@ function _handleChange(event) {
var name = props.name;
if (props.type === 'radio' && name != null) {
var rootNode = ReactMount.getNode(this._rootNodeID);
var rootNode = ReactDOMComponentTree.getNodeFromInstance(this);
var queryRoot = rootNode;
while (queryRoot.parentNode) {
@@ -12,7 +12,7 @@
'use strict';
var LinkedValueUtils = require('LinkedValueUtils');
var ReactMount = require('ReactMount');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
var assign = require('Object.assign');
@@ -112,7 +112,7 @@ function checkSelectPropTypes(inst, props) {
*/
function updateOptions(inst, multiple, propValue) {
var selectedValue, i;
var options = ReactMount.getNode(inst._rootNodeID).options;
var options = ReactDOMComponentTree.getNodeFromInstance(inst).options;
if (multiple) {
selectedValue = {};
@@ -11,8 +11,9 @@
'use strict';
var DOMPropertyOperations = require('DOMPropertyOperations');
var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMIDOperations = require('ReactDOMIDOperations');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
var assign = require('Object.assign');
@@ -144,8 +145,8 @@ var ReactDOMTextarea = {
if (value != null) {
// Cast `value` to a string to ensure the value is set correctly. While
// browsers typically do this as necessary, jsdom doesn't.
ReactDOMIDOperations.updatePropertyByID(
inst._rootNodeID,
DOMPropertyOperations.setValueForProperty(
ReactDOMComponentTree.getNodeFromInstance(inst),
'value',
'' + value
);
+21 -18
View File
@@ -27,6 +27,8 @@ var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactComponentBrowserEnvironment =
require('ReactComponentBrowserEnvironment');
var ReactDOMButton = require('ReactDOMButton');
var ReactDOMComponentFlags = require('ReactDOMComponentFlags');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMInput = require('ReactDOMInput');
var ReactDOMOption = require('ReactDOMOption');
var ReactDOMSelect = require('ReactDOMSelect');
@@ -46,7 +48,9 @@ var shallowEqual = require('shallowEqual');
var validateDOMNesting = require('validateDOMNesting');
var warning = require('warning');
var Flags = ReactDOMComponentFlags;
var deleteListener = EventPluginHub.deleteListener;
var getNode = ReactDOMComponentTree.getNodeFromInstance;
var listenTo = ReactBrowserEventEmitter.listenTo;
var registrationNameModules = EventPluginRegistry.registrationNameModules;
@@ -493,14 +497,6 @@ function isCustomComponent(tagName, props) {
return tagName.indexOf('-') >= 0 || props.is != null;
}
function getNode(inst) {
if (inst._nativeNode) {
return inst._nativeNode;
} else {
return inst._nativeNode = ReactMount.getNode(inst._rootNodeID);
}
}
/**
* Creates a new React class that is idempotent and capable of containing other
* React components. It accepts event listeners and DOM properties that are
@@ -523,11 +519,12 @@ function ReactDOMComponent(tag) {
this._previousStyle = null;
this._previousStyleCopy = null;
this._nativeNode = null;
this._nativeParent = null;
this._rootNodeID = null;
this._nativeContainerInfo = null;
this._wrapperState = null;
this._topLevelWrapper = null;
this._nodeHasLegacyProperties = false;
this._flags = 0;
if (__DEV__) {
this._ancestorInfo = null;
}
@@ -561,6 +558,7 @@ ReactDOMComponent.Mixin = {
context
) {
this._rootNodeID = rootID;
this._nativeParent = nativeParent;
this._nativeContainerInfo = nativeContainerInfo;
var props = this._currentElement.props;
@@ -664,6 +662,7 @@ ReactDOMComponent.Mixin = {
);
}
this._nativeNode = el;
this._flags |= Flags.hasCachedChildNodes;
DOMPropertyOperations.setAttributeForID(el, this._rootNodeID);
// Populate node cache
ReactMount.getID(el);
@@ -907,7 +906,7 @@ ReactDOMComponent.Mixin = {
context
);
if (!canDefineProperty && this._nodeHasLegacyProperties) {
if (!canDefineProperty && this._flags & Flags.nodeHasLegacyProperties) {
this._nativeNode.props = nextProps;
}
@@ -1146,7 +1145,7 @@ ReactDOMComponent.Mixin = {
break;
}
if (this._nodeHasLegacyProperties) {
if (this._flags & Flags.nodeHasLegacyProperties) {
this._nativeNode._reactInternalComponent = null;
}
this._nativeNode = null;
@@ -1159,7 +1158,7 @@ ReactDOMComponent.Mixin = {
},
getPublicInstance: function() {
if (this._nodeHasLegacyProperties) {
if (this._flags & Flags.nodeHasLegacyProperties) {
return this._nativeNode;
} else {
var node = getNode(this);
@@ -1185,7 +1184,7 @@ ReactDOMComponent.Mixin = {
node.props = this._currentElement.props;
}
this._nodeHasLegacyProperties = true;
this._flags |= Flags.nodeHasLegacyProperties;
return node;
}
},
@@ -1200,11 +1199,15 @@ ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', {
assign(
ReactDOMComponent.prototype,
ReactDOMComponent.Mixin,
ReactMultiChild.Mixin
ReactMultiChild.Mixin,
{
prepareToManageChildren: function() {
// Before we add, remove, or reorder the children of a node, make sure
// we have references to all of its children so we don't lose them, even
// if nefarious browser plugins add extra nodes to our tree.
ReactDOMComponentTree.precacheChildNodes(this, getNode(this));
},
}
);
assign(ReactDOMComponent, {
getNodeFromInstance: getNode,
});
module.exports = ReactDOMComponent;
@@ -0,0 +1,19 @@
/**
* Copyright 2015, 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 ReactDOMComponentFlags
*/
'use strict';
var ReactDOMComponentFlags = {
nodeHasLegacyProperties: 1 << 0,
hasCachedChildNodes: 1 << 1,
};
module.exports = ReactDOMComponentFlags;
@@ -17,19 +17,14 @@ var DOMLazyTree = require('DOMLazyTree');
var DOMPropertyOperations = require('DOMPropertyOperations');
var ReactComponentBrowserEnvironment =
require('ReactComponentBrowserEnvironment');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactMount = require('ReactMount');
var assign = require('Object.assign');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var validateDOMNesting = require('validateDOMNesting');
function getNode(inst) {
if (inst._nativeNode) {
return inst._nativeNode;
} else {
return inst._nativeNode = ReactMount.getNode(inst._rootNodeID);
}
}
var getNode = ReactDOMComponentTree.getNodeFromInstance;
/**
* Text nodes violate a couple assumptions that React makes about components:
@@ -61,6 +56,8 @@ assign(ReactDOMTextComponent.prototype, {
this._currentElement = text;
this._stringText = '' + text;
this._nativeNode = null;
// ReactDOMComponentTree uses this:
this._nativeParent = null;
// Properties
this._rootNodeID = null;
@@ -98,6 +95,7 @@ assign(ReactDOMTextComponent.prototype, {
}
this._rootNodeID = rootID;
this._nativeParent = nativeParent;
if (transaction.useCreateElement) {
var ownerDocument = nativeContainerInfo._ownerDocument;
var el = ownerDocument.createElement('span');
@@ -51,15 +51,15 @@ var markupQueue = [];
/**
* Enqueues markup to be rendered and inserted at a supplied index.
*
* @param {string} parentID ID of the parent component.
* @param {object} parentInst parent component.
* @param {string} markup Markup that renders into an element.
* @param {number} toIndex Destination index.
* @private
*/
function enqueueInsertMarkup(parentID, markup, toIndex) {
function enqueueInsertMarkup(parentInst, markup, toIndex) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
markupIndex: markupQueue.push(markup) - 1,
@@ -72,15 +72,15 @@ function enqueueInsertMarkup(parentID, markup, toIndex) {
/**
* Enqueues moving an existing element to another index.
*
* @param {string} parentID ID of the parent component.
* @param {object} parentInst parent component.
* @param {number} fromIndex Source index of the existing element.
* @param {number} toIndex Destination index of the element.
* @private
*/
function enqueueMove(parentID, fromIndex, toIndex) {
function enqueueMove(parentInst, fromIndex, toIndex) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.MOVE_EXISTING,
markupIndex: null,
@@ -93,14 +93,14 @@ function enqueueMove(parentID, fromIndex, toIndex) {
/**
* Enqueues removing an element at an index.
*
* @param {string} parentID ID of the parent component.
* @param {object} parentInst parent component.
* @param {number} fromIndex Index of the element to remove.
* @private
*/
function enqueueRemove(parentID, fromIndex) {
function enqueueRemove(parentInst, fromIndex) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.REMOVE_NODE,
markupIndex: null,
@@ -113,14 +113,14 @@ function enqueueRemove(parentID, fromIndex) {
/**
* Enqueues setting the markup of a node.
*
* @param {string} parentID ID of the parent component.
* @param {object} parentInst parent component.
* @param {string} markup Markup that renders into an element.
* @private
*/
function enqueueSetMarkup(parentID, markup) {
function enqueueSetMarkup(parentInst, markup) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.SET_MARKUP,
markupIndex: null,
@@ -133,14 +133,14 @@ function enqueueSetMarkup(parentID, markup) {
/**
* Enqueues setting the text content.
*
* @param {string} parentID ID of the parent component.
* @param {object} parentInst parent component.
* @param {string} textContent Text content to set.
* @private
*/
function enqueueTextContent(parentID, textContent) {
function enqueueTextContent(parentInst, textContent) {
// NOTE: Null values reduce hidden classes.
updateQueue.push({
parentID: parentID,
parentInst: parentInst,
parentNode: null,
type: ReactMultiChildUpdateTypes.TEXT_CONTENT,
markupIndex: null,
@@ -371,7 +371,6 @@ var ReactMultiChild = {
var nextChildren = this._reconcilerUpdateChildren(
prevChildren, nextNestedChildrenElements, transaction, context
);
this._renderedChildren = nextChildren;
if (!nextChildren && !prevChildren) {
return;
}
@@ -410,6 +409,7 @@ var ReactMultiChild = {
this._unmountChild(prevChildren[name]);
}
}
this._renderedChildren = nextChildren;
},
/**
@@ -424,6 +424,14 @@ var ReactMultiChild = {
this._renderedChildren = null;
},
/**
* Hook used by the DOM implementation to precache the nodes before we apply
* any reorders here.
*/
prepareToManageChildren: function() {
// TODO: This sucks. Figure out a better design here.
},
/**
* Moves a child component to the supplied index.
*
@@ -437,7 +445,8 @@ var ReactMultiChild = {
// be moved. Otherwise, we do not need to move it because a child will be
// inserted or moved before `child`.
if (child._mountIndex < lastIndex) {
enqueueMove(this._rootNodeID, child._mountIndex, toIndex);
this.prepareToManageChildren();
enqueueMove(this, child._mountIndex, toIndex);
}
},
@@ -449,7 +458,8 @@ var ReactMultiChild = {
* @protected
*/
createChild: function(child, mountImage) {
enqueueInsertMarkup(this._rootNodeID, mountImage, child._mountIndex);
this.prepareToManageChildren();
enqueueInsertMarkup(this, mountImage, child._mountIndex);
},
/**
@@ -459,7 +469,8 @@ var ReactMultiChild = {
* @protected
*/
removeChild: function(child) {
enqueueRemove(this._rootNodeID, child._mountIndex);
this.prepareToManageChildren();
enqueueRemove(this, child._mountIndex);
},
/**
@@ -469,7 +480,7 @@ var ReactMultiChild = {
* @protected
*/
setTextContent: function(textContent) {
enqueueTextContent(this._rootNodeID, textContent);
enqueueTextContent(this, textContent);
},
/**
@@ -479,7 +490,7 @@ var ReactMultiChild = {
* @protected
*/
setMarkup: function(markup) {
enqueueSetMarkup(this._rootNodeID, markup);
enqueueSetMarkup(this, markup);
},
/**
+1 -1
View File
@@ -194,7 +194,7 @@ var ReactDefaultPerf = {
writeArgs.markup = args[1][update.markupIndex];
}
ReactDefaultPerf._recordWrite(
update.parentID,
update.parentInst._rootNodeID,
update.type,
totalTime,
writeArgs