diff --git a/src/isomorphic/classic/element/ReactElement.js b/src/isomorphic/classic/element/ReactElement.js index 42b7e37cab3bb..9e333270e2f5c 100644 --- a/src/isomorphic/classic/element/ReactElement.js +++ b/src/isomorphic/classic/element/ReactElement.js @@ -14,71 +14,12 @@ var ReactCurrentOwner = require('ReactCurrentOwner'); var assign = require('Object.assign'); -var warning = require('warning'); var RESERVED_PROPS = { key: true, ref: true, }; -/** - * Warn for mutations. - * - * @internal - * @param {object} object - * @param {string} key - */ -function defineWarningProperty(object, key) { - Object.defineProperty(object, key, { - - configurable: false, - enumerable: true, - - get: function() { - if (!this._store) { - return null; - } - return this._store[key]; - }, - - set: function(value) { - warning( - false, - 'Don\'t set the %s property of the React element. Instead, ' + - 'specify the correct value when initially creating the element.', - key - ); - this._store[key] = value; - }, - - }); -} - -/** - * This is updated to true if the membrane is successfully created. - */ -var useMutationMembrane = false; - -/** - * Warn for mutations. - * - * @internal - * @param {object} prototype - */ -function defineMutationMembrane(prototype) { - try { - var pseudoFrozenProperties = { - props: true, - }; - for (var key in pseudoFrozenProperties) { - defineWarningProperty(prototype, key); - } - useMutationMembrane = true; - } catch (x) { - // IE will fail on defineProperty - } -} - /** * Base constructor for all React elements. This is only used to make this * work with a dynamic instanceof check. Nothing should live on this prototype. @@ -100,11 +41,11 @@ var ReactElement = function(type, key, ref, owner, props) { this._owner = owner; if (__DEV__) { - // The validation flag and props are currently mutative. We put them on + // The validation flag is currently mutative. We put it on // an external backing store so that we can freeze the whole object. // This can be replaced with a WeakMap once they are implemented in // commonly used development environments. - this._store = {props: props, originalProps: assign({}, props)}; + this._store = {}; // To make comparing ReactElements easier for testing purposes, we make // the validation flag non-enumerable (where possible, which should @@ -115,21 +56,15 @@ var ReactElement = function(type, key, ref, owner, props) { configurable: false, enumerable: false, writable: true, + value: false, }); } catch (x) { + this._store.validated = false; } - this._store.validated = false; - - // We're not allowed to set props directly on the object so we early - // return and rely on the prototype membrane to forward to the backing - // store. - if (useMutationMembrane) { - Object.freeze(this); - return; - } + this.props = props; + Object.freeze(this.props); + Object.freeze(this); } - - this.props = props; }; // We intentionally don't expose the function on the constructor property. @@ -138,10 +73,6 @@ ReactElement.prototype = { _isReactElement: true, }; -if (__DEV__) { - defineMutationMembrane(ReactElement.prototype); -} - ReactElement.createElement = function(type, config, children) { var propName; @@ -219,6 +150,7 @@ ReactElement.cloneAndReplaceProps = function(oldElement, newProps) { // If the key on the original is valid, then the clone is valid newElement._store.validated = oldElement._store.validated; } + return newElement; }; diff --git a/src/isomorphic/classic/element/ReactElementValidator.js b/src/isomorphic/classic/element/ReactElementValidator.js index a7067ec0457ff..d4c2df2a077df 100644 --- a/src/isomorphic/classic/element/ReactElementValidator.js +++ b/src/isomorphic/classic/element/ReactElementValidator.js @@ -286,90 +286,6 @@ function checkPropTypes(componentName, propTypes, props, location) { } } -var warnedPropsMutations = {}; - -/** - * Warn about mutating props when setting `propName` on `element`. - * - * @param {string} propName The string key within props that was set - * @param {ReactElement} element - */ -function warnForPropsMutation(propName, element) { - var type = element.type; - var elementName = typeof type === 'string' ? type : type.displayName; - var ownerName = element._owner ? - element._owner.getPublicInstance().constructor.displayName : null; - - var warningKey = propName + '|' + elementName + '|' + ownerName; - if (warnedPropsMutations.hasOwnProperty(warningKey)) { - return; - } - warnedPropsMutations[warningKey] = true; - - var elementInfo = ''; - if (elementName) { - elementInfo = ' <' + elementName + ' />'; - } - var ownerInfo = ''; - if (ownerName) { - ownerInfo = ' The element was created by ' + ownerName + '.'; - } - - warning( - false, - 'Don\'t set .props.%s of the React component%s. Instead, specify the ' + - 'correct value when initially creating the element or use ' + - 'React.cloneElement to make a new element with updated props.%s', - propName, - elementInfo, - ownerInfo - ); -} - -// Inline Object.is polyfill -function is(a, b) { - if (a !== a) { - // NaN - return b !== b; - } - if (a === 0 && b === 0) { - // +-0 - return 1 / a === 1 / b; - } - return a === b; -} - -/** - * Given an element, check if its props have been mutated since element - * creation (or the last call to this function). In particular, check if any - * new props have been added, which we can't directly catch by defining warning - * properties on the props object. - * - * @param {ReactElement} element - */ -function checkAndWarnForMutatedProps(element) { - if (!element._store) { - // Element was created using `new ReactElement` directly or with - // `ReactElement.createElement`; skip mutation checking - return; - } - - var originalProps = element._store.originalProps; - var props = element.props; - - for (var propName in props) { - if (props.hasOwnProperty(propName)) { - if (!originalProps.hasOwnProperty(propName) || - !is(originalProps[propName], props[propName])) { - warnForPropsMutation(propName, element); - - // Copy over the new value so that the two props objects match again - originalProps[propName] = props[propName]; - } - } - } -} - /** * Given an element, validate that its props follow the propTypes definition, * provided by the type. @@ -409,8 +325,6 @@ function validatePropTypes(element) { var ReactElementValidator = { - checkAndWarnForMutatedProps: checkAndWarnForMutatedProps, - createElement: function(type, props, children) { // We warn in this case but don't throw. We expect the element creation to // succeed and there will likely be errors in render. diff --git a/src/isomorphic/classic/element/__tests__/ReactElement-test.js b/src/isomorphic/classic/element/__tests__/ReactElement-test.js index 0cb9f82a84d78..1e715e1f45579 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElement-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElement-test.js @@ -37,7 +37,9 @@ describe('ReactElement', function() { expect(element.type).toBe(ComponentClass); expect(element.key).toBe(null); expect(element.ref).toBe(null); - expect(element.props).toEqual({}); + var expectation = {}; + Object.freeze(expectation); + expect(element.props).toEqual(expectation); }); it('allows a string to be passed as the type', function() { @@ -45,7 +47,9 @@ describe('ReactElement', function() { expect(element.type).toBe('div'); expect(element.key).toBe(null); expect(element.ref).toBe(null); - expect(element.props).toEqual({}); + var expectation = {}; + Object.freeze(expectation); + expect(element.props).toEqual(expectation); }); it('returns an immutable element', function() { @@ -70,7 +74,9 @@ describe('ReactElement', function() { expect(element.type).toBe(ComponentClass); expect(element.key).toBe('12'); expect(element.ref).toBe('34'); - expect(element.props).toEqual({foo:'56'}); + var expectation = {foo:'56'}; + Object.freeze(expectation); + expect(element.props).toEqual(expectation); }); it('coerces the key to a string', function() { @@ -81,7 +87,9 @@ describe('ReactElement', function() { expect(element.type).toBe(ComponentClass); expect(element.key).toBe('12'); expect(element.ref).toBe(null); - expect(element.props).toEqual({foo:'56'}); + var expectation = {foo:'56'}; + Object.freeze(expectation); + expect(element.props).toEqual(expectation); }); it('preserves the owner on the element', function() { @@ -243,84 +251,42 @@ describe('ReactElement', function() { expect(inst2.props.prop).toBe(null); }); - it('warns when changing a prop after element creation', function() { - spyOn(console, 'error'); + it('throws when changing a prop (in dev) after element creation', function() { var Outer = React.createClass({ render: function() { var el =
; - // This assignment warns but should still work for now. - el.props.className = 'quack'; - expect(el.props.className).toBe('quack'); + expect(function() { + el.props.className = 'quack'; + }).toThrow(); + expect(el.props.className).toBe('moo'); return el; }, }); var outer = ReactTestUtils.renderIntoDocument(