changed ReactChildrenMutationWarningHook to Object.freeze (#7455)

- only freeze children array created by createElement
(cherry picked from commit 38c4ade6cc)
This commit is contained in:
Keyan Zhang
2016-09-13 09:25:31 -04:00
committed by Paul O’Shannessy
parent 4d99f2bdd4
commit ec4c0f1dd6
5 changed files with 50 additions and 121 deletions
@@ -135,7 +135,6 @@ var ReactElement = function(type, key, ref, self, source, owner, props) {
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};
var shadowChildren = Array.isArray(props.children) ? props.children.slice(0) : props.children;
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
@@ -155,12 +154,6 @@ var ReactElement = function(type, key, ref, self, source, owner, props) {
writable: false,
value: self,
});
Object.defineProperty(element, '_shadowChildren', {
configurable: false,
enumerable: false,
writable: false,
value: shadowChildren,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
@@ -172,7 +165,6 @@ var ReactElement = function(type, key, ref, self, source, owner, props) {
} else {
element._store.validated = false;
element._self = self;
element._shadowChildren = shadowChildren;
element._source = source;
}
if (Object.freeze) {
@@ -228,6 +220,11 @@ ReactElement.createElement = function(type, config, children) {
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
@@ -539,37 +539,20 @@ describe('ReactServerRendering', () => {
expect(markup).toBe('<div></div>');
});
it('warns when children are mutated before render', () => {
function normalizeCodeLocInfo(str) {
return str.replace(/\(at .+?:\d+\)/g, '(at **)');
}
it('should warn when children are mutated during render', () => {
spyOn(console, 'error');
var children = [<span key={0} />, <span key={1} />, <span key={2} />];
var element = <div>{children}</div>;
children[1] = <p key={1} />; // Mutation is illegal
ReactServerRendering.renderToString(element);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Component\'s children should not be mutated.\n in div (at **)'
);
});
it('should warn when children are mutated', () => {
function normalizeCodeLocInfo(str) {
return str.replace(/\(at .+?:\d+\)/g, '(at **)');
}
spyOn(console, 'error');
var children = [<span key={0} />, <span key={1} />, <span key={2} />];
function Wrapper(props) {
props.children[1] = <p key={1} />; // Mutation is illegal
return <div>{props.children}</div>;
}
ReactServerRendering.renderToString(<Wrapper>{children}</Wrapper>);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Component\'s children should not be mutated.\n in Wrapper (at **)'
);
expect(() => {
ReactServerRendering.renderToStaticMarkup(
<Wrapper>
<span key={0}/>
<span key={1}/>
<span key={2}/>
</Wrapper>
);
}).toThrowError(/Cannot assign to read only property.*/);
});
});
-2
View File
@@ -15,7 +15,6 @@
var ReactInvalidSetStateWarningHook = require('ReactInvalidSetStateWarningHook');
var ReactHostOperationHistoryHook = require('ReactHostOperationHistoryHook');
var ReactComponentTreeHook = require('ReactComponentTreeHook');
var ReactChildrenMutationWarningHook = require('ReactChildrenMutationWarningHook');
var ExecutionEnvironment = require('ExecutionEnvironment');
var performanceNow = require('performanceNow');
@@ -417,7 +416,6 @@ var ReactDebugTool = {
ReactDebugTool.addHook(ReactInvalidSetStateWarningHook);
ReactDebugTool.addHook(ReactComponentTreeHook);
ReactDebugTool.addHook(ReactChildrenMutationWarningHook);
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if ((/[?&]react_perf\b/).test(url)) {
ReactDebugTool.beginProfiling();
@@ -1,61 +0,0 @@
/**
* 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.
*
* @providesModule ReactChildrenMutationWarningHook
* @flow
*/
'use strict';
var ReactComponentTreeHook = require('ReactComponentTreeHook');
var warning = require('warning');
import type { DebugID } from 'ReactInstanceType';
function handleElement(debugID, element) {
if (element == null) {
return;
}
if (element._shadowChildren === undefined) {
return;
}
if (element._shadowChildren === element.props.children) {
return;
}
var isMutated = false;
if (Array.isArray(element._shadowChildren)) {
if (element._shadowChildren.length === element.props.children.length) {
for (var i = 0; i < element._shadowChildren.length; i++) {
if (element._shadowChildren[i] !== element.props.children[i]) {
isMutated = true;
}
}
} else {
isMutated = true;
}
}
if (!Array.isArray(element._shadowChildren) || isMutated) {
warning(
false,
'Component\'s children should not be mutated.%s',
ReactComponentTreeHook.getStackAddendumByID(debugID),
);
}
}
var ReactChildrenMutationWarningHook = {
onMountComponent(debugID: DebugID): void {
handleElement(debugID, ReactComponentTreeHook.getElement(debugID));
},
onUpdateComponent(debugID: DebugID): void {
handleElement(debugID, ReactComponentTreeHook.getElement(debugID));
},
};
module.exports = ReactChildrenMutationWarningHook;
@@ -15,10 +15,6 @@ var React;
var ReactDOM;
var ReactTestUtils;
function normalizeCodeLocInfo(str) {
return str.replace(/\(at .+?:\d+\)/g, '(at **)');
}
describe('ReactComponent', () => {
beforeEach(() => {
React = require('React');
@@ -49,30 +45,46 @@ describe('ReactComponent', () => {
}).toThrow();
});
it('should warn when children are mutated before render', () => {
it('should warn when children are mutated during render', () => {
spyOn(console, 'error');
var children = [<span key={0} />, <span key={1} />, <span key={2} />];
var element = <div>{children}</div>;
children[1] = <p key={1} />; // Mutation is illegal
ReactTestUtils.renderIntoDocument(element);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Component\'s children should not be mutated.\n in div (at **)'
);
});
it('should warn when children are mutated', () => {
spyOn(console, 'error');
var children = [<span key={0} />, <span key={1} />, <span key={2} />];
function Wrapper(props) {
props.children[1] = <p key={1} />; // Mutation is illegal
return <div>{props.children}</div>;
}
ReactTestUtils.renderIntoDocument(<Wrapper>{children}</Wrapper>);
expect(console.error.calls.count()).toBe(1);
expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: Component\'s children should not be mutated.\n in Wrapper (at **)'
);
expect(() => {
ReactTestUtils.renderIntoDocument(
<Wrapper>
<span key={0}/>
<span key={1}/>
<span key={2}/>
</Wrapper>
);
}).toThrowError(/Cannot assign to read only property.*/);
});
it('should warn when children are mutated during update', () => {
spyOn(console, 'error');
class Wrapper extends React.Component {
componentDidMount() {
this.props.children[1] = <p key={1} />; // Mutation is illegal
this.forceUpdate();
}
render() {
return <div>{this.props.children}</div>;
}
}
expect(() => {
ReactTestUtils.renderIntoDocument(
<Wrapper>
<span key={0}/>
<span key={1}/>
<span key={2}/>
</Wrapper>
);
}).toThrowError(/Cannot assign to read only property.*/);
});
it('should support refs on owned components', () => {