aboutsummaryrefslogtreecommitdiff
path: root/node_modules/react-dom/lib/ReactMultiChild.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/react-dom/lib/ReactMultiChild.js')
-rw-r--r--node_modules/react-dom/lib/ReactMultiChild.js449
1 files changed, 449 insertions, 0 deletions
diff --git a/node_modules/react-dom/lib/ReactMultiChild.js b/node_modules/react-dom/lib/ReactMultiChild.js
new file mode 100644
index 000000000..e52005ff2
--- /dev/null
+++ b/node_modules/react-dom/lib/ReactMultiChild.js
@@ -0,0 +1,449 @@
+/**
+ * Copyright 2013-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 _prodInvariant = require('./reactProdInvariant');
+
+var ReactComponentEnvironment = require('./ReactComponentEnvironment');
+var ReactInstanceMap = require('./ReactInstanceMap');
+var ReactInstrumentation = require('./ReactInstrumentation');
+
+var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
+var ReactReconciler = require('./ReactReconciler');
+var ReactChildReconciler = require('./ReactChildReconciler');
+
+var emptyFunction = require('fbjs/lib/emptyFunction');
+var flattenChildren = require('./flattenChildren');
+var invariant = require('fbjs/lib/invariant');
+
+/**
+ * Make an update for markup to be rendered and inserted at a supplied index.
+ *
+ * @param {string} markup Markup that renders into an element.
+ * @param {number} toIndex Destination index.
+ * @private
+ */
+function makeInsertMarkup(markup, afterNode, toIndex) {
+ // NOTE: Null values reduce hidden classes.
+ return {
+ type: 'INSERT_MARKUP',
+ content: markup,
+ fromIndex: null,
+ fromNode: null,
+ toIndex: toIndex,
+ afterNode: afterNode
+ };
+}
+
+/**
+ * Make an update for moving an existing element to another index.
+ *
+ * @param {number} fromIndex Source index of the existing element.
+ * @param {number} toIndex Destination index of the element.
+ * @private
+ */
+function makeMove(child, afterNode, toIndex) {
+ // NOTE: Null values reduce hidden classes.
+ return {
+ type: 'MOVE_EXISTING',
+ content: null,
+ fromIndex: child._mountIndex,
+ fromNode: ReactReconciler.getHostNode(child),
+ toIndex: toIndex,
+ afterNode: afterNode
+ };
+}
+
+/**
+ * Make an update for removing an element at an index.
+ *
+ * @param {number} fromIndex Index of the element to remove.
+ * @private
+ */
+function makeRemove(child, node) {
+ // NOTE: Null values reduce hidden classes.
+ return {
+ type: 'REMOVE_NODE',
+ content: null,
+ fromIndex: child._mountIndex,
+ fromNode: node,
+ toIndex: null,
+ afterNode: null
+ };
+}
+
+/**
+ * Make an update for setting the markup of a node.
+ *
+ * @param {string} markup Markup that renders into an element.
+ * @private
+ */
+function makeSetMarkup(markup) {
+ // NOTE: Null values reduce hidden classes.
+ return {
+ type: 'SET_MARKUP',
+ content: markup,
+ fromIndex: null,
+ fromNode: null,
+ toIndex: null,
+ afterNode: null
+ };
+}
+
+/**
+ * Make an update for setting the text content.
+ *
+ * @param {string} textContent Text content to set.
+ * @private
+ */
+function makeTextContent(textContent) {
+ // NOTE: Null values reduce hidden classes.
+ return {
+ type: 'TEXT_CONTENT',
+ content: textContent,
+ fromIndex: null,
+ fromNode: null,
+ toIndex: null,
+ afterNode: null
+ };
+}
+
+/**
+ * Push an update, if any, onto the queue. Creates a new queue if none is
+ * passed and always returns the queue. Mutative.
+ */
+function enqueue(queue, update) {
+ if (update) {
+ queue = queue || [];
+ queue.push(update);
+ }
+ return queue;
+}
+
+/**
+ * Processes any enqueued updates.
+ *
+ * @private
+ */
+function processQueue(inst, updateQueue) {
+ ReactComponentEnvironment.processChildrenUpdates(inst, updateQueue);
+}
+
+var setChildrenForInstrumentation = emptyFunction;
+if (process.env.NODE_ENV !== 'production') {
+ var getDebugID = function (inst) {
+ if (!inst._debugID) {
+ // Check for ART-like instances. TODO: This is silly/gross.
+ var internal;
+ if (internal = ReactInstanceMap.get(inst)) {
+ inst = internal;
+ }
+ }
+ return inst._debugID;
+ };
+ setChildrenForInstrumentation = function (children) {
+ var debugID = getDebugID(this);
+ // TODO: React Native empty components are also multichild.
+ // This means they still get into this method but don't have _debugID.
+ if (debugID !== 0) {
+ ReactInstrumentation.debugTool.onSetChildren(debugID, children ? Object.keys(children).map(function (key) {
+ return children[key]._debugID;
+ }) : []);
+ }
+ };
+}
+
+/**
+ * ReactMultiChild are capable of reconciling multiple children.
+ *
+ * @class ReactMultiChild
+ * @internal
+ */
+var ReactMultiChild = {
+
+ /**
+ * Provides common functionality for components that must reconcile multiple
+ * children. This is used by `ReactDOMComponent` to mount, update, and
+ * unmount child components.
+ *
+ * @lends {ReactMultiChild.prototype}
+ */
+ Mixin: {
+
+ _reconcilerInstantiateChildren: function (nestedChildren, transaction, context) {
+ if (process.env.NODE_ENV !== 'production') {
+ var selfDebugID = getDebugID(this);
+ if (this._currentElement) {
+ try {
+ ReactCurrentOwner.current = this._currentElement._owner;
+ return ReactChildReconciler.instantiateChildren(nestedChildren, transaction, context, selfDebugID);
+ } finally {
+ ReactCurrentOwner.current = null;
+ }
+ }
+ }
+ return ReactChildReconciler.instantiateChildren(nestedChildren, transaction, context);
+ },
+
+ _reconcilerUpdateChildren: function (prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context) {
+ var nextChildren;
+ var selfDebugID = 0;
+ if (process.env.NODE_ENV !== 'production') {
+ selfDebugID = getDebugID(this);
+ if (this._currentElement) {
+ try {
+ ReactCurrentOwner.current = this._currentElement._owner;
+ nextChildren = flattenChildren(nextNestedChildrenElements, selfDebugID);
+ } finally {
+ ReactCurrentOwner.current = null;
+ }
+ ReactChildReconciler.updateChildren(prevChildren, nextChildren, mountImages, removedNodes, transaction, this, this._hostContainerInfo, context, selfDebugID);
+ return nextChildren;
+ }
+ }
+ nextChildren = flattenChildren(nextNestedChildrenElements, selfDebugID);
+ ReactChildReconciler.updateChildren(prevChildren, nextChildren, mountImages, removedNodes, transaction, this, this._hostContainerInfo, context, selfDebugID);
+ return nextChildren;
+ },
+
+ /**
+ * 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} nestedChildren Nested child maps.
+ * @return {array} An array of mounted representations.
+ * @internal
+ */
+ mountChildren: function (nestedChildren, transaction, context) {
+ var children = this._reconcilerInstantiateChildren(nestedChildren, transaction, context);
+ this._renderedChildren = children;
+
+ var mountImages = [];
+ var index = 0;
+ for (var name in children) {
+ if (children.hasOwnProperty(name)) {
+ var child = children[name];
+ var selfDebugID = 0;
+ if (process.env.NODE_ENV !== 'production') {
+ selfDebugID = getDebugID(this);
+ }
+ var mountImage = ReactReconciler.mountComponent(child, transaction, this, this._hostContainerInfo, context, selfDebugID);
+ child._mountIndex = index++;
+ mountImages.push(mountImage);
+ }
+ }
+
+ if (process.env.NODE_ENV !== 'production') {
+ setChildrenForInstrumentation.call(this, children);
+ }
+
+ return mountImages;
+ },
+
+ /**
+ * Replaces any rendered children with a text content string.
+ *
+ * @param {string} nextContent String of content.
+ * @internal
+ */
+ updateTextContent: function (nextContent) {
+ var prevChildren = this._renderedChildren;
+ // Remove any rendered children.
+ ReactChildReconciler.unmountChildren(prevChildren, false);
+ for (var name in prevChildren) {
+ if (prevChildren.hasOwnProperty(name)) {
+ !false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'updateTextContent called on non-empty component.') : _prodInvariant('118') : void 0;
+ }
+ }
+ // Set new text content.
+ var updates = [makeTextContent(nextContent)];
+ processQueue(this, updates);
+ },
+
+ /**
+ * Replaces any rendered children with a markup string.
+ *
+ * @param {string} nextMarkup String of markup.
+ * @internal
+ */
+ updateMarkup: function (nextMarkup) {
+ var prevChildren = this._renderedChildren;
+ // Remove any rendered children.
+ ReactChildReconciler.unmountChildren(prevChildren, false);
+ for (var name in prevChildren) {
+ if (prevChildren.hasOwnProperty(name)) {
+ !false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'updateTextContent called on non-empty component.') : _prodInvariant('118') : void 0;
+ }
+ }
+ var updates = [makeSetMarkup(nextMarkup)];
+ processQueue(this, updates);
+ },
+
+ /**
+ * Updates the rendered children with new children.
+ *
+ * @param {?object} nextNestedChildrenElements Nested child element maps.
+ * @param {ReactReconcileTransaction} transaction
+ * @internal
+ */
+ updateChildren: function (nextNestedChildrenElements, transaction, context) {
+ // Hook used by React ART
+ this._updateChildren(nextNestedChildrenElements, transaction, context);
+ },
+
+ /**
+ * @param {?object} nextNestedChildrenElements Nested child element maps.
+ * @param {ReactReconcileTransaction} transaction
+ * @final
+ * @protected
+ */
+ _updateChildren: function (nextNestedChildrenElements, transaction, context) {
+ var prevChildren = this._renderedChildren;
+ var removedNodes = {};
+ var mountImages = [];
+ var nextChildren = this._reconcilerUpdateChildren(prevChildren, nextNestedChildrenElements, mountImages, removedNodes, transaction, context);
+ if (!nextChildren && !prevChildren) {
+ return;
+ }
+ var updates = null;
+ var name;
+ // `nextIndex` will increment for each child in `nextChildren`, but
+ // `lastIndex` will be the last index visited in `prevChildren`.
+ var nextIndex = 0;
+ var lastIndex = 0;
+ // `nextMountIndex` will increment for each newly mounted child.
+ var nextMountIndex = 0;
+ var lastPlacedNode = null;
+ for (name in nextChildren) {
+ if (!nextChildren.hasOwnProperty(name)) {
+ continue;
+ }
+ var prevChild = prevChildren && prevChildren[name];
+ var nextChild = nextChildren[name];
+ if (prevChild === nextChild) {
+ updates = enqueue(updates, this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex));
+ lastIndex = Math.max(prevChild._mountIndex, lastIndex);
+ prevChild._mountIndex = nextIndex;
+ } else {
+ if (prevChild) {
+ // Update `lastIndex` before `_mountIndex` gets unset by unmounting.
+ lastIndex = Math.max(prevChild._mountIndex, lastIndex);
+ // The `removedNodes` loop below will actually remove the child.
+ }
+ // The child must be instantiated before it's mounted.
+ updates = enqueue(updates, this._mountChildAtIndex(nextChild, mountImages[nextMountIndex], lastPlacedNode, nextIndex, transaction, context));
+ nextMountIndex++;
+ }
+ nextIndex++;
+ lastPlacedNode = ReactReconciler.getHostNode(nextChild);
+ }
+ // Remove children that are no longer present.
+ for (name in removedNodes) {
+ if (removedNodes.hasOwnProperty(name)) {
+ updates = enqueue(updates, this._unmountChild(prevChildren[name], removedNodes[name]));
+ }
+ }
+ if (updates) {
+ processQueue(this, updates);
+ }
+ this._renderedChildren = nextChildren;
+
+ if (process.env.NODE_ENV !== 'production') {
+ setChildrenForInstrumentation.call(this, nextChildren);
+ }
+ },
+
+ /**
+ * Unmounts all rendered children. This should be used to clean up children
+ * when this component is unmounted. It does not actually perform any
+ * backend operations.
+ *
+ * @internal
+ */
+ unmountChildren: function (safely) {
+ var renderedChildren = this._renderedChildren;
+ ReactChildReconciler.unmountChildren(renderedChildren, safely);
+ this._renderedChildren = null;
+ },
+
+ /**
+ * Moves a child component to the supplied index.
+ *
+ * @param {ReactComponent} child Component to move.
+ * @param {number} toIndex Destination index of the element.
+ * @param {number} lastIndex Last index visited of the siblings of `child`.
+ * @protected
+ */
+ moveChild: function (child, afterNode, toIndex, lastIndex) {
+ // If the index of `child` is less than `lastIndex`, then it needs to
+ // be moved. Otherwise, we do not need to move it because a child will be
+ // inserted or moved before `child`.
+ if (child._mountIndex < lastIndex) {
+ return makeMove(child, afterNode, toIndex);
+ }
+ },
+
+ /**
+ * Creates a child component.
+ *
+ * @param {ReactComponent} child Component to create.
+ * @param {string} mountImage Markup to insert.
+ * @protected
+ */
+ createChild: function (child, afterNode, mountImage) {
+ return makeInsertMarkup(mountImage, afterNode, child._mountIndex);
+ },
+
+ /**
+ * Removes a child component.
+ *
+ * @param {ReactComponent} child Child to remove.
+ * @protected
+ */
+ removeChild: function (child, node) {
+ return makeRemove(child, node);
+ },
+
+ /**
+ * Mounts a child with the supplied name.
+ *
+ * NOTE: This is part of `updateChildren` and is here for readability.
+ *
+ * @param {ReactComponent} child Component to mount.
+ * @param {string} name Name of the child.
+ * @param {number} index Index at which to insert the child.
+ * @param {ReactReconcileTransaction} transaction
+ * @private
+ */
+ _mountChildAtIndex: function (child, mountImage, afterNode, index, transaction, context) {
+ child._mountIndex = index;
+ return this.createChild(child, afterNode, mountImage);
+ },
+
+ /**
+ * Unmounts a rendered child.
+ *
+ * NOTE: This is part of `updateChildren` and is here for readability.
+ *
+ * @param {ReactComponent} child Component to unmount.
+ * @private
+ */
+ _unmountChild: function (child, node) {
+ var update = this.removeChild(child, node);
+ child._mountIndex = null;
+ return update;
+ }
+
+ }
+
+};
+
+module.exports = ReactMultiChild; \ No newline at end of file