mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Separate Child Reconciliation Step from Diffing
This separates the reconciliation step of children into a separate module. This is the first step towards prerendering. The stateful instances are reconciled and put into a "rendered children" set. Updates creates a new of these sets. These two sets are then diffed to create insert/move/remove operations on the set. The next step is to move the ReactChildReconciler step to before the native DOM component. That way it's possible to rely on child reconciliation without relying on diffing.
This commit is contained in:
121
src/core/ReactChildReconciler.js
Normal file
121
src/core/ReactChildReconciler.js
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright 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 ReactChildReconciler
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var flattenChildren = require('flattenChildren');
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
|
||||
/**
|
||||
* ReactChildReconciler provides helpers for initializing or updating a set of
|
||||
* children. Its output is suitable for passing it onto ReactMultiChild which
|
||||
* does diffed reordering and insertion.
|
||||
*/
|
||||
var ReactChildReconciler = {
|
||||
|
||||
/**
|
||||
* Generates a "mount image" for each of the supplied children. In the case
|
||||
* of `ReactDOMComponent`, a mount image is a string of markup.
|
||||
*
|
||||
* @param {?object} nestedChildNodes Nested child maps.
|
||||
* @return {?object} A set of child instances.
|
||||
* @internal
|
||||
*/
|
||||
instantiateChildren: function(nestedChildNodes, transaction, context) {
|
||||
var children = flattenChildren(nestedChildNodes);
|
||||
for (var name in children) {
|
||||
if (children.hasOwnProperty(name)) {
|
||||
var child = children[name];
|
||||
// The rendered children must be turned into instances as they're
|
||||
// mounted.
|
||||
var childInstance = instantiateReactComponent(child, null);
|
||||
children[name] = childInstance;
|
||||
}
|
||||
}
|
||||
return children;
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the rendered children and returns a new set of children.
|
||||
*
|
||||
* @param {?object} prevChildren Previously initialized set of children.
|
||||
* @param {?object} nextNestedChildNodes Nested child maps.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} context
|
||||
* @return {?object} A new set of child instances.
|
||||
* @internal
|
||||
*/
|
||||
updateChildren: function(
|
||||
prevChildren,
|
||||
nextNestedChildNodes,
|
||||
transaction,
|
||||
context) {
|
||||
// We currently don't have a way to track moves here but if we use iterators
|
||||
// instead of for..in we can zip the iterators and check if an item has
|
||||
// moved.
|
||||
// TODO: If nothing has changed, return the prevChildren object so that we
|
||||
// can quickly bailout if nothing has changed.
|
||||
var nextChildren = flattenChildren(nextNestedChildNodes);
|
||||
if (!nextChildren && !prevChildren) {
|
||||
return;
|
||||
}
|
||||
var name;
|
||||
for (name in nextChildren) {
|
||||
if (!nextChildren.hasOwnProperty(name)) {
|
||||
continue;
|
||||
}
|
||||
var prevChild = prevChildren && prevChildren[name];
|
||||
var prevElement = prevChild && prevChild._currentElement;
|
||||
var nextElement = nextChildren[name];
|
||||
if (shouldUpdateReactComponent(prevElement, nextElement)) {
|
||||
prevChild.receiveComponent(nextElement, transaction, context);
|
||||
nextChildren[name] = prevChild;
|
||||
} else {
|
||||
if (prevChild) {
|
||||
prevChild.unmountComponent();
|
||||
}
|
||||
// The child must be instantiated before it's mounted.
|
||||
var nextChildInstance = instantiateReactComponent(
|
||||
nextElement,
|
||||
null
|
||||
);
|
||||
nextChildren[name] = nextChildInstance;
|
||||
}
|
||||
}
|
||||
// Unmount children that are no longer present.
|
||||
for (name in prevChildren) {
|
||||
if (prevChildren.hasOwnProperty(name) &&
|
||||
!(nextChildren && nextChildren.hasOwnProperty(name))) {
|
||||
prevChildren[name].unmountComponent();
|
||||
}
|
||||
}
|
||||
return nextChildren;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unmounts all rendered children. This should be used to clean up children
|
||||
* when this component is unmounted.
|
||||
*
|
||||
* @param {?object} renderedChildren Previously initialized set of children.
|
||||
* @internal
|
||||
*/
|
||||
unmountChildren: function(renderedChildren) {
|
||||
for (var name in renderedChildren) {
|
||||
var renderedChild = renderedChildren[name];
|
||||
renderedChild.unmountComponent();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactChildReconciler;
|
||||
@@ -15,9 +15,7 @@
|
||||
var ReactComponentEnvironment = require('ReactComponentEnvironment');
|
||||
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
|
||||
|
||||
var flattenChildren = require('flattenChildren');
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
var ReactChildReconciler = require('ReactChildReconciler');
|
||||
|
||||
/**
|
||||
* Updating children of a component may trigger recursive updates. The depth is
|
||||
@@ -179,25 +177,23 @@ var ReactMultiChild = {
|
||||
* @internal
|
||||
*/
|
||||
mountChildren: function(nestedChildren, transaction, context) {
|
||||
var children = flattenChildren(nestedChildren);
|
||||
var children = ReactChildReconciler.instantiateChildren(
|
||||
nestedChildren, transaction, context
|
||||
);
|
||||
this._renderedChildren = children;
|
||||
var mountImages = [];
|
||||
var index = 0;
|
||||
this._renderedChildren = children;
|
||||
for (var name in children) {
|
||||
var child = children[name];
|
||||
if (children.hasOwnProperty(name)) {
|
||||
// The rendered children must be turned into instances as they're
|
||||
// mounted.
|
||||
var childInstance = instantiateReactComponent(child, null);
|
||||
children[name] = childInstance;
|
||||
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
|
||||
var rootID = this._rootNodeID + name;
|
||||
var mountImage = childInstance.mountComponent(
|
||||
var mountImage = child.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
context
|
||||
);
|
||||
childInstance._mountIndex = index;
|
||||
child._mountIndex = index;
|
||||
mountImages.push(mountImage);
|
||||
index++;
|
||||
}
|
||||
@@ -217,6 +213,8 @@ var ReactMultiChild = {
|
||||
try {
|
||||
var prevChildren = this._renderedChildren;
|
||||
// Remove any rendered children.
|
||||
ReactChildReconciler.unmountChildren(prevChildren);
|
||||
// TODO: The setTextContent operation should be enough
|
||||
for (var name in prevChildren) {
|
||||
if (prevChildren.hasOwnProperty(name)) {
|
||||
this._unmountChildByName(prevChildren[name], name);
|
||||
@@ -264,8 +262,11 @@ var ReactMultiChild = {
|
||||
* @protected
|
||||
*/
|
||||
_updateChildren: function(nextNestedChildren, transaction, context) {
|
||||
var nextChildren = flattenChildren(nextNestedChildren);
|
||||
var prevChildren = this._renderedChildren;
|
||||
var nextChildren = ReactChildReconciler.updateChildren(
|
||||
prevChildren, nextNestedChildren, transaction, context
|
||||
);
|
||||
this._renderedChildren = nextChildren;
|
||||
if (!nextChildren && !prevChildren) {
|
||||
return;
|
||||
}
|
||||
@@ -279,12 +280,10 @@ var ReactMultiChild = {
|
||||
continue;
|
||||
}
|
||||
var prevChild = prevChildren && prevChildren[name];
|
||||
var prevElement = prevChild && prevChild._currentElement;
|
||||
var nextElement = nextChildren[name];
|
||||
if (shouldUpdateReactComponent(prevElement, nextElement)) {
|
||||
var nextChild = nextChildren[name];
|
||||
if (prevChild === nextChild) {
|
||||
this.moveChild(prevChild, nextIndex, lastIndex);
|
||||
lastIndex = Math.max(prevChild._mountIndex, lastIndex);
|
||||
prevChild.receiveComponent(nextElement, transaction, context);
|
||||
prevChild._mountIndex = nextIndex;
|
||||
} else {
|
||||
if (prevChild) {
|
||||
@@ -293,12 +292,8 @@ var ReactMultiChild = {
|
||||
this._unmountChildByName(prevChild, name);
|
||||
}
|
||||
// The child must be instantiated before it's mounted.
|
||||
var nextChildInstance = instantiateReactComponent(
|
||||
nextElement,
|
||||
null
|
||||
);
|
||||
this._mountChildByNameAtIndex(
|
||||
nextChildInstance, name, nextIndex, transaction, context
|
||||
nextChild, name, nextIndex, transaction, context
|
||||
);
|
||||
}
|
||||
nextIndex++;
|
||||
@@ -320,13 +315,7 @@ var ReactMultiChild = {
|
||||
*/
|
||||
unmountChildren: function() {
|
||||
var renderedChildren = this._renderedChildren;
|
||||
for (var name in renderedChildren) {
|
||||
var renderedChild = renderedChildren[name];
|
||||
// TODO: When is this not true?
|
||||
if (renderedChild.unmountComponent) {
|
||||
renderedChild.unmountComponent();
|
||||
}
|
||||
}
|
||||
ReactChildReconciler.unmountChildren(renderedChildren);
|
||||
this._renderedChildren = null;
|
||||
},
|
||||
|
||||
@@ -404,8 +393,6 @@ var ReactMultiChild = {
|
||||
);
|
||||
child._mountIndex = index;
|
||||
this.createChild(child, mountImage);
|
||||
this._renderedChildren = this._renderedChildren || {};
|
||||
this._renderedChildren[name] = child;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -420,8 +407,6 @@ var ReactMultiChild = {
|
||||
_unmountChildByName: function(child, name) {
|
||||
this.removeChild(child);
|
||||
child._mountIndex = null;
|
||||
child.unmountComponent();
|
||||
delete this._renderedChildren[name];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user