@samitouri / QOS-React-2 / commits / 39402fe75d

Revert "Add key warning to nested collections"

This heuristic isn't great because it relies on inspecting deep children which aren't guaranteed to be React elements. In particular, this was causing stack overflows in a component we had that used a *DOM node* as children, like `<DOMContainer>{node}</DOMContainer>`. This reverts commits: 0a3aa8493aa014668f5dd3bac30868c18397538e 64c9d9d7629600da81ceefcaa59c46e64d030874 0c58f4f6b12bd7856b73f14163db905cb13c5fc7 8cf226e44241aeafe147f6256a1351b46ac3cf91 086636747f26b577b4a4577a0888118310ee91b3

Ben Alpert committed May 14, 2015 at 16:33 UTC 39402fe75d3657615aa9b4ae0be73dab2665321e
4 files changed +37 -111
src/addons/__tests__/ReactFragment-test.js
+3 -3
@@ -49,13 +49,13 @@ describe('ReactFragment', function() {
49 z: <span />
50 };
51 var element = <div>{[children]}</div>;
52 + expect(console.error.calls.length).toBe(0);
53 + var container = document.createElement('div');
54 + React.render(element, container);
55 expect(console.error.calls.length).toBe(1);
56 expect(console.error.calls[0].args[0]).toContain(
57 'Any use of a keyed object'
58 );
56 - var container = document.createElement('div');
57 - React.render(element, container);
58 - expect(console.error.calls.length).toBe(1);
59 });
60
61 it('should warn if accessing any property on a fragment', function() {
src/isomorphic/classic/element/ReactElement.js
+19 -21
@@ -80,21 +80,6 @@ function defineMutationMembrane(prototype) {
80 }
81 }
82
83 -function markChildArrayValidated(childArray) {
84 - // To make comparing ReactElements easier for testing purposes, we make the
85 - // validation flag non-enumerable (where possible, which should include every
86 - // environment we run tests in), so the test framework ignores it.
87 - try {
88 - Object.defineProperty(childArray, '_reactChildKeysValidated', {
89 - configurable: false,
90 - enumerable: false,
91 - writable: true
92 - });
93 - } catch (x) {
94 - }
95 - childArray._reactChildKeysValidated = true;
96 -}
97 -
83 /**
84 * Base constructor for all React elements. This is only used to make this
85 * work with a dynamic instanceof check. Nothing should live on this prototype.
@@ -121,6 +106,20 @@ var ReactElement = function(type, key, ref, owner, context, props) {
106 // commonly used development environments.
107 this._store = {props: props, originalProps: assign({}, props)};
108
109 + // To make comparing ReactElements easier for testing purposes, we make
110 + // the validation flag non-enumerable (where possible, which should
111 + // include every environment we run tests in), so the test framework
112 + // ignores it.
113 + try {
114 + Object.defineProperty(this._store, 'validated', {
115 + configurable: false,
116 + enumerable: false,
117 + writable: true
118 + });
119 + } catch (x) {
120 + }
121 + this._store.validated = false;
122 +
123 // We're not allowed to set props directly on the object so we early
124 // return and rely on the prototype membrane to forward to the backing
125 // store.
@@ -171,9 +170,6 @@ ReactElement.createElement = function(type, config, children) {
170 props.children = children;
171 } else if (childrenLength > 1) {
172 var childArray = Array(childrenLength);
174 - if (__DEV__) {
175 - markChildArrayValidated(childArray);
176 - }
173 for (var i = 0; i < childrenLength; i++) {
174 childArray[i] = arguments[i + 2];
175 }
@@ -220,6 +216,11 @@ ReactElement.cloneAndReplaceProps = function(oldElement, newProps) {
216 oldElement._context,
217 newProps
218 );
219 +
220 + if (__DEV__) {
221 + // If the key on the original is valid, then the clone is valid
222 + newElement._store.validated = oldElement._store.validated;
223 + }
224 return newElement;
225 };
226
@@ -261,9 +262,6 @@ ReactElement.cloneElement = function(element, config, children) {
262 props.children = children;
263 } else if (childrenLength > 1) {
264 var childArray = Array(childrenLength);
264 - if (__DEV__) {
265 - markChildArrayValidated(childArray);
266 - }
265 for (var i = 0; i < childrenLength; i++) {
266 childArray[i] = arguments[i + 2];
267 }
src/isomorphic/classic/element/ReactElementValidator.js
+15 -37
@@ -90,19 +90,15 @@ function getCurrentOwnerDisplayName() {
90 * @internal
91 * @param {ReactElement} element Element that requires a key.
92 * @param {*} parentType element's parent's type.
93 - * @param {boolean} deep false if top-level collection, true if nested
93 */
95 -function validateExplicitKey(element, parentType, deep) {
96 - if (element.key != null) {
94 +function validateExplicitKey(element, parentType) {
95 + if (element._store.validated || element.key != null) {
96 return;
97 }
98 + element._store.validated = true;
99 +
100 warnAndMonitorForKeyUse(
100 - // We vary the message for nested key warning to allow filtering them out
101 - // since we didn't historically warn in this case.
102 - deep ?
103 - 'Each child in a nested array or iterator should have ' +
104 - 'a unique "key" prop.' :
105 - 'Each child in an array or iterator should have a unique "key" prop.',
101 + 'Each child in an array or iterator should have a unique "key" prop.',
102 element,
103 parentType
104 );
@@ -116,17 +112,13 @@ function validateExplicitKey(element, parentType, deep) {
112 * @param {string} name Property name of the key.
113 * @param {ReactElement} element Component that requires a key.
114 * @param {*} parentType element's parent's type.
119 - * @param {boolean} deep false if top-level collection, true if nested
115 */
121 -function validatePropertyKey(name, element, parentType, deep) {
116 +function validatePropertyKey(name, element, parentType) {
117 if (!NUMERIC_PROPERTY_REGEX.test(name)) {
118 return;
119 }
120 warnAndMonitorForKeyUse(
126 - deep ?
127 - 'Nested child objects should have non-numeric keys ' +
128 - 'so ordering is preserved.' :
129 - 'Child objects should have non-numeric keys so ordering is preserved.',
121 + 'Child objects should have non-numeric keys so ordering is preserved.',
122 element,
123 parentType
124 );
@@ -188,29 +180,18 @@ function warnAndMonitorForKeyUse(message, element, parentType) {
180 * @internal
181 * @param {ReactNode} node Statically passed child of any type.
182 * @param {*} parentType node's parent's type.
191 - * @param {boolean} deep false if top-level collection, true if nested
183 */
193 -function validateChildKeys(node, parentType, deep) {
184 +function validateChildKeys(node, parentType) {
185 if (Array.isArray(node)) {
195 - if (node._reactChildKeysValidated) {
196 - // All child elements were passed in a valid location.
197 - return;
198 - }
186 for (var i = 0; i < node.length; i++) {
187 var child = node[i];
188 if (ReactElement.isValidElement(child)) {
202 - validateExplicitKey(child, parentType, deep);
203 - } else {
204 - // TODO: Warn on unkeyed arrays and suggest using createFragment
205 - validateChildKeys(child, parentType, true);
189 + validateExplicitKey(child, parentType);
190 }
191 }
208 - } else if (
209 - typeof node === 'string' || typeof node === 'number' ||
210 - ReactElement.isValidElement(node)
211 - ) {
192 + } else if (ReactElement.isValidElement(node)) {
193 // This element was passed in a valid location.
213 - return;
194 + node._store.validated = true;
195 } else if (node) {
196 var iteratorFn = getIteratorFn(node);
197 // Entry iterators provide implicit keys.
@@ -220,9 +201,7 @@ function validateChildKeys(node, parentType, deep) {
201 var step;
202 while (!(step = iterator.next()).done) {
203 if (ReactElement.isValidElement(step.value)) {
223 - validateExplicitKey(step.value, parentType, deep);
224 - } else {
225 - validateChildKeys(step.value, parentType, true);
204 + validateExplicitKey(step.value, parentType);
205 }
206 }
207 }
@@ -230,8 +209,7 @@ function validateChildKeys(node, parentType, deep) {
209 var fragment = ReactFragment.extractIfFragment(node);
210 for (var key in fragment) {
211 if (fragment.hasOwnProperty(key)) {
233 - validatePropertyKey(key, fragment[key], parentType, deep);
234 - validateChildKeys(fragment[key], parentType, true);
212 + validatePropertyKey(key, fragment[key], parentType);
213 }
214 }
215 }
@@ -437,7 +415,7 @@ var ReactElementValidator = {
415 }
416
417 for (var i = 2; i < arguments.length; i++) {
440 - validateChildKeys(arguments[i], type, false);
418 + validateChildKeys(arguments[i], type);
419 }
420
421 validatePropTypes(element);
@@ -485,7 +463,7 @@ var ReactElementValidator = {
463 cloneElement: function(element, props, children) {
464 var newElement = ReactElement.cloneElement.apply(this, arguments);
465 for (var i = 2; i < arguments.length; i++) {
488 - validateChildKeys(arguments[i], newElement.type, false);
466 + validateChildKeys(arguments[i], newElement.type);
467 }
468 validatePropTypes(newElement);
469 return newElement;
src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
-50
@@ -120,56 +120,6 @@ describe('ReactElementValidator', function() {
120 );
121 });
122
123 - it('warns for keys for nested arrays of elements', function() {
124 - spyOn(console, 'error');
125 -
126 - var divs = [
127 - [
128 - <div />,
129 - <div />
130 - ],
131 - <div key="foo" />
132 - ];
133 - ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
134 -
135 - expect(console.error.argsForCall.length).toBe(1);
136 - expect(console.error.argsForCall[0][0]).toBe(
137 - 'Warning: Each child in a nested array or iterator should have a ' +
138 - 'unique "key" prop. Check the React.render call using <div>. See ' +
139 - 'https://fb.me/react-warning-keys for more information.'
140 - );
141 - });
142 -
143 - it('warns for keys when reusing children', function() {
144 - spyOn(console, 'error');
145 -
146 - var f = <span />;
147 - var g = <span />;
148 -
149 - var children = [f, g];
150 -
151 - ReactTestUtils.renderIntoDocument(
152 - <div>
153 - <div key="0">
154 - {g}
155 - </div>
156 - <div key="1">
157 - {f}
158 - </div>
159 - <div key="2">
160 - {children}
161 - </div>
162 - </div>
163 - );
164 -
165 - expect(console.error.argsForCall.length).toBe(1);
166 - expect(console.error.argsForCall[0][0]).toBe(
167 - 'Warning: Each child in an array or iterator should have a unique ' +
168 - '"key" prop. Check the React.render call using <div>. See ' +
169 - 'https://fb.me/react-warning-keys for more information.'
170 - );
171 - });
172 -
123 it('does not warn for keys when passing children down', function() {
124 spyOn(console, 'error');
125