UNPKG

5.98 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2014-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var ReactReconciler = require('./ReactReconciler');
12
13var instantiateReactComponent = require('./instantiateReactComponent');
14var KeyEscapeUtils = require('./KeyEscapeUtils');
15var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
16var traverseAllChildren = require('./traverseAllChildren');
17var warning = require('fbjs/lib/warning');
18
19var ReactComponentTreeHook;
20
21if (typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'test') {
22 // Temporary hack.
23 // Inline requires don't work well with Jest:
24 // https://github.com/facebook/react/issues/7240
25 // Remove the inline requires when we don't need them anymore:
26 // https://github.com/facebook/react/pull/7178
27 ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
28}
29
30function instantiateChild(childInstances, child, name, selfDebugID) {
31 // We found a component instance.
32 var keyUnique = childInstances[name] === undefined;
33 if (process.env.NODE_ENV !== 'production') {
34 if (!ReactComponentTreeHook) {
35 ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
36 }
37 if (!keyUnique) {
38 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;
39 }
40 }
41 if (child != null && keyUnique) {
42 childInstances[name] = instantiateReactComponent(child, true);
43 }
44}
45
46/**
47 * ReactChildReconciler provides helpers for initializing or updating a set of
48 * children. Its output is suitable for passing it onto ReactMultiChild which
49 * does diffed reordering and insertion.
50 */
51var ReactChildReconciler = {
52 /**
53 * Generates a "mount image" for each of the supplied children. In the case
54 * of `ReactDOMComponent`, a mount image is a string of markup.
55 *
56 * @param {?object} nestedChildNodes Nested child maps.
57 * @return {?object} A set of child instances.
58 * @internal
59 */
60 instantiateChildren: function (nestedChildNodes, transaction, context, selfDebugID) // 0 in production and for roots
61 {
62 if (nestedChildNodes == null) {
63 return null;
64 }
65 var childInstances = {};
66
67 if (process.env.NODE_ENV !== 'production') {
68 traverseAllChildren(nestedChildNodes, function (childInsts, child, name) {
69 return instantiateChild(childInsts, child, name, selfDebugID);
70 }, childInstances);
71 } else {
72 traverseAllChildren(nestedChildNodes, instantiateChild, childInstances);
73 }
74 return childInstances;
75 },
76
77 /**
78 * Updates the rendered children and returns a new set of children.
79 *
80 * @param {?object} prevChildren Previously initialized set of children.
81 * @param {?object} nextChildren Flat child element maps.
82 * @param {ReactReconcileTransaction} transaction
83 * @param {object} context
84 * @return {?object} A new set of child instances.
85 * @internal
86 */
87 updateChildren: function (prevChildren, nextChildren, mountImages, removedNodes, transaction, hostParent, hostContainerInfo, context, selfDebugID) // 0 in production and for roots
88 {
89 // We currently don't have a way to track moves here but if we use iterators
90 // instead of for..in we can zip the iterators and check if an item has
91 // moved.
92 // TODO: If nothing has changed, return the prevChildren object so that we
93 // can quickly bailout if nothing has changed.
94 if (!nextChildren && !prevChildren) {
95 return;
96 }
97 var name;
98 var prevChild;
99 for (name in nextChildren) {
100 if (!nextChildren.hasOwnProperty(name)) {
101 continue;
102 }
103 prevChild = prevChildren && prevChildren[name];
104 var prevElement = prevChild && prevChild._currentElement;
105 var nextElement = nextChildren[name];
106 if (prevChild != null && shouldUpdateReactComponent(prevElement, nextElement)) {
107 ReactReconciler.receiveComponent(prevChild, nextElement, transaction, context);
108 nextChildren[name] = prevChild;
109 } else {
110 if (prevChild) {
111 removedNodes[name] = ReactReconciler.getHostNode(prevChild);
112 ReactReconciler.unmountComponent(prevChild, false);
113 }
114 // The child must be instantiated before it's mounted.
115 var nextChildInstance = instantiateReactComponent(nextElement, true);
116 nextChildren[name] = nextChildInstance;
117 // Creating mount image now ensures refs are resolved in right order
118 // (see https://github.com/facebook/react/pull/7101 for explanation).
119 var nextChildMountImage = ReactReconciler.mountComponent(nextChildInstance, transaction, hostParent, hostContainerInfo, context, selfDebugID);
120 mountImages.push(nextChildMountImage);
121 }
122 }
123 // Unmount children that are no longer present.
124 for (name in prevChildren) {
125 if (prevChildren.hasOwnProperty(name) && !(nextChildren && nextChildren.hasOwnProperty(name))) {
126 prevChild = prevChildren[name];
127 removedNodes[name] = ReactReconciler.getHostNode(prevChild);
128 ReactReconciler.unmountComponent(prevChild, false);
129 }
130 }
131 },
132
133 /**
134 * Unmounts all rendered children. This should be used to clean up children
135 * when this component is unmounted.
136 *
137 * @param {?object} renderedChildren Previously initialized set of children.
138 * @internal
139 */
140 unmountChildren: function (renderedChildren, safely) {
141 for (var name in renderedChildren) {
142 if (renderedChildren.hasOwnProperty(name)) {
143 var renderedChild = renderedChildren[name];
144 ReactReconciler.unmountComponent(renderedChild, safely);
145 }
146 }
147 }
148};
149
150module.exports = ReactChildReconciler;
\No newline at end of file