mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
changed ReactChildrenMutationWarningHook to Object.freeze (#7455)
- only freeze children array created by createElement
(cherry picked from commit 38c4ade6cc)
This commit is contained in:
committed by
Paul O’Shannessy
parent
4d99f2bdd4
commit
ec4c0f1dd6
@@ -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.*/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user