153 lines
6.0 KiB
JavaScript
153 lines
6.0 KiB
JavaScript
|
/**
|
||
|
* Copyright 2014-present, 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.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var ReactReconciler = require('./ReactReconciler');
|
||
|
|
||
|
var instantiateReactComponent = require('./instantiateReactComponent');
|
||
|
var KeyEscapeUtils = require('./KeyEscapeUtils');
|
||
|
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
|
||
|
var traverseAllChildren = require('./traverseAllChildren');
|
||
|
var warning = require('fbjs/lib/warning');
|
||
|
|
||
|
var ReactComponentTreeHook;
|
||
|
|
||
|
if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'test') {
|
||
|
// Temporary hack.
|
||
|
// Inline requires don't work well with Jest:
|
||
|
// https://github.com/facebook/react/issues/7240
|
||
|
// Remove the inline requires when we don't need them anymore:
|
||
|
// https://github.com/facebook/react/pull/7178
|
||
|
ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
|
||
|
}
|
||
|
|
||
|
function instantiateChild(childInstances, child, name, selfDebugID) {
|
||
|
// We found a component instance.
|
||
|
var keyUnique = childInstances[name] === undefined;
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
if (!ReactComponentTreeHook) {
|
||
|
ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
|
||
|
}
|
||
|
if (!keyUnique) {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
|
||
|
}
|
||
|
}
|
||
|
if (child != null && keyUnique) {
|
||
|
childInstances[name] = instantiateReactComponent(child, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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, selfDebugID // 0 in production and for roots
|
||
|
) {
|
||
|
if (nestedChildNodes == null) {
|
||
|
return null;
|
||
|
}
|
||
|
var childInstances = {};
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
traverseAllChildren(nestedChildNodes, function (childInsts, child, name) {
|
||
|
return instantiateChild(childInsts, child, name, selfDebugID);
|
||
|
}, childInstances);
|
||
|
} else {
|
||
|
traverseAllChildren(nestedChildNodes, instantiateChild, childInstances);
|
||
|
}
|
||
|
return childInstances;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Updates the rendered children and returns a new set of children.
|
||
|
*
|
||
|
* @param {?object} prevChildren Previously initialized set of children.
|
||
|
* @param {?object} nextChildren Flat child element maps.
|
||
|
* @param {ReactReconcileTransaction} transaction
|
||
|
* @param {object} context
|
||
|
* @return {?object} A new set of child instances.
|
||
|
* @internal
|
||
|
*/
|
||
|
updateChildren: function (prevChildren, nextChildren, mountImages, removedNodes, transaction, hostParent, hostContainerInfo, context, selfDebugID // 0 in production and for roots
|
||
|
) {
|
||
|
// 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.
|
||
|
if (!nextChildren && !prevChildren) {
|
||
|
return;
|
||
|
}
|
||
|
var name;
|
||
|
var prevChild;
|
||
|
for (name in nextChildren) {
|
||
|
if (!nextChildren.hasOwnProperty(name)) {
|
||
|
continue;
|
||
|
}
|
||
|
prevChild = prevChildren && prevChildren[name];
|
||
|
var prevElement = prevChild && prevChild._currentElement;
|
||
|
var nextElement = nextChildren[name];
|
||
|
if (prevChild != null && shouldUpdateReactComponent(prevElement, nextElement)) {
|
||
|
ReactReconciler.receiveComponent(prevChild, nextElement, transaction, context);
|
||
|
nextChildren[name] = prevChild;
|
||
|
} else {
|
||
|
if (prevChild) {
|
||
|
removedNodes[name] = ReactReconciler.getHostNode(prevChild);
|
||
|
ReactReconciler.unmountComponent(prevChild, false);
|
||
|
}
|
||
|
// The child must be instantiated before it's mounted.
|
||
|
var nextChildInstance = instantiateReactComponent(nextElement, true);
|
||
|
nextChildren[name] = nextChildInstance;
|
||
|
// Creating mount image now ensures refs are resolved in right order
|
||
|
// (see https://github.com/facebook/react/pull/7101 for explanation).
|
||
|
var nextChildMountImage = ReactReconciler.mountComponent(nextChildInstance, transaction, hostParent, hostContainerInfo, context, selfDebugID);
|
||
|
mountImages.push(nextChildMountImage);
|
||
|
}
|
||
|
}
|
||
|
// Unmount children that are no longer present.
|
||
|
for (name in prevChildren) {
|
||
|
if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) {
|
||
|
prevChild = prevChildren[name];
|
||
|
removedNodes[name] = ReactReconciler.getHostNode(prevChild);
|
||
|
ReactReconciler.unmountComponent(prevChild, false);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* 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, safely) {
|
||
|
for (var name in renderedChildren) {
|
||
|
if (renderedChildren.hasOwnProperty(name)) {
|
||
|
var renderedChild = renderedChildren[name];
|
||
|
ReactReconciler.unmountComponent(renderedChild, safely);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
module.exports = ReactChildReconciler;
|