From 4eb889b72ee13402d1a0b10e9458c95d7445f7e1 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Fri, 3 Jul 2015 10:09:25 +0200 Subject: [PATCH] Revert "Removed setProps and replaceProps (both previously deprecated)" This reverts commit 721fe7354142c866db2ebcc727a2472a3f3e0cc6. We'll keep these with a warning for one more release. --- .../__tests__/ReactContextValidator-test.js | 6 +- src/isomorphic/classic/class/ReactClass.js | 32 +++++++ src/isomorphic/modern/class/ReactComponent.js | 8 ++ .../modern/class/ReactNoopUpdateQueue.js | 23 +++++ .../ReactCoffeeScriptClass-test.coffee | 10 +- .../class/__tests__/ReactES6Class-test.js | 10 +- .../__tests__/ReactTypeScriptClass-test.ts | 10 +- src/renderers/dom/shared/ReactDOMComponent.js | 41 +++++++++ .../__tests__/ReactDOMComponent-test.js | 23 +++++ .../shared/reconciler/ReactUpdateQueue.js | 91 ++++++++++++++++++- .../shared/reconciler/ReactUpdates.js | 2 +- .../__tests__/ReactComponentLifeCycle-test.js | 66 ++++++++++++-- .../__tests__/ReactCompositeComponent-test.js | 74 ++++++++++++++- .../ReactCompositeComponentState-test.js | 5 +- .../ReactMultiChildReconcile-test.js | 28 ++---- .../reconciler/__tests__/ReactUpdates-test.js | 2 + 16 files changed, 386 insertions(+), 45 deletions(-) diff --git a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js index 027c536826..e2e5aee48b 100644 --- a/src/isomorphic/classic/__tests__/ReactContextValidator-test.js +++ b/src/isomorphic/classic/__tests__/ReactContextValidator-test.js @@ -121,9 +121,9 @@ describe('ReactContextValidator', function() { }, }); - var container = document.createElement('div'); - React.render(, container); - React.render(, container); + var instance = ; + instance = ReactTestUtils.renderIntoDocument(instance); + instance.replaceProps({foo: 'def'}); expect(actualComponentWillReceiveProps).toEqual({foo: 'def'}); expect(actualShouldComponentUpdate).toEqual({foo: 'def'}); expect(actualComponentWillUpdate).toEqual({foo: 'def'}); diff --git a/src/isomorphic/classic/class/ReactClass.js b/src/isomorphic/classic/class/ReactClass.js index 717cec67a0..73ebdab706 100644 --- a/src/isomorphic/classic/class/ReactClass.js +++ b/src/isomorphic/classic/class/ReactClass.js @@ -728,6 +728,38 @@ var ReactClassMixin = { isMounted: function() { return this.updater.isMounted(this); }, + + /** + * Sets a subset of the props. + * + * @param {object} partialProps Subset of the next props. + * @param {?function} callback Called after props are updated. + * @final + * @public + * @deprecated + */ + setProps: function(partialProps, callback) { + this.updater.enqueueSetProps(this, partialProps); + if (callback) { + this.updater.enqueueCallback(this, callback); + } + }, + + /** + * Replace all the props. + * + * @param {object} newProps Subset of the next props. + * @param {?function} callback Called after props are updated. + * @final + * @public + * @deprecated + */ + replaceProps: function(newProps, callback) { + this.updater.enqueueReplaceProps(this, newProps); + if (callback) { + this.updater.enqueueCallback(this, callback); + } + }, }; var ReactClassComponent = function() {}; diff --git a/src/isomorphic/modern/class/ReactComponent.js b/src/isomorphic/modern/class/ReactComponent.js index 80b3e655bf..ddf7bc14ef 100644 --- a/src/isomorphic/modern/class/ReactComponent.js +++ b/src/isomorphic/modern/class/ReactComponent.js @@ -112,11 +112,19 @@ if (__DEV__) { 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.', ], + replaceProps: [ + 'replaceProps', + 'Instead, call React.render again at the top level.', + ], replaceState: [ 'replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).', ], + setProps: [ + 'setProps', + 'Instead, call React.render again at the top level.', + ], }; var defineDeprecationWarning = function(methodName, info) { try { diff --git a/src/isomorphic/modern/class/ReactNoopUpdateQueue.js b/src/isomorphic/modern/class/ReactNoopUpdateQueue.js index 42346794bc..69fc3ddb1b 100644 --- a/src/isomorphic/modern/class/ReactNoopUpdateQueue.js +++ b/src/isomorphic/modern/class/ReactNoopUpdateQueue.js @@ -98,6 +98,29 @@ var ReactNoopUpdateQueue = { enqueueSetState: function(publicInstance, partialState) { warnTDZ(publicInstance, 'setState'); }, + + /** + * Sets a subset of the props. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {object} partialProps Subset of the next props. + * @internal + */ + enqueueSetProps: function(publicInstance, partialProps) { + warnTDZ(publicInstance, 'setProps'); + }, + + /** + * Replaces all of the props. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {object} props New props. + * @internal + */ + enqueueReplaceProps: function(publicInstance, props) { + warnTDZ(publicInstance, 'replaceProps'); + }, + }; module.exports = ReactNoopUpdateQueue; diff --git a/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee b/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee index bcbf267547..2538cc39aa 100644 --- a/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee +++ b/src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee @@ -346,7 +346,9 @@ describe 'ReactCoffeeScriptClass', -> expect(-> instance.getDOMNode()).toThrow() expect(-> instance.replaceState {}).toThrow() expect(-> instance.isMounted()).toThrow() - expect(console.error.calls.length).toBe 3 + expect(-> instance.setProps name: 'bar').toThrow() + expect(-> instance.replaceProps name: 'bar').toThrow() + expect(console.error.calls.length).toBe 5 expect(console.error.calls[0].args[0]).toContain( 'getDOMNode(...) is deprecated in plain JavaScript React classes' ) @@ -356,6 +358,12 @@ describe 'ReactCoffeeScriptClass', -> expect(console.error.calls[2].args[0]).toContain( 'isMounted(...) is deprecated in plain JavaScript React classes' ) + expect(console.error.calls[3].args[0]).toContain( + 'setProps(...) is deprecated in plain JavaScript React classes' + ) + expect(console.error.calls[4].args[0]).toContain( + 'replaceProps(...) is deprecated in plain JavaScript React classes' + ) it 'supports this.context passed via getChildContext', -> class Bar diff --git a/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js b/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js index 59cfe38ee4..0530c5bfb4 100644 --- a/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js +++ b/src/isomorphic/modern/class/__tests__/ReactES6Class-test.js @@ -404,7 +404,9 @@ describe('ReactES6Class', function() { expect(() => instance.getDOMNode()).toThrow(); expect(() => instance.replaceState({})).toThrow(); expect(() => instance.isMounted()).toThrow(); - expect(console.error.calls.length).toBe(3); + expect(() => instance.setProps({name: 'bar'})).toThrow(); + expect(() => instance.replaceProps({name: 'bar'})).toThrow(); + expect(console.error.calls.length).toBe(5); expect(console.error.calls[0].args[0]).toContain( 'getDOMNode(...) is deprecated in plain JavaScript React classes. ' + 'Use React.findDOMNode(component) instead.' @@ -415,6 +417,12 @@ describe('ReactES6Class', function() { expect(console.error.calls[2].args[0]).toContain( 'isMounted(...) is deprecated in plain JavaScript React classes' ); + expect(console.error.calls[3].args[0]).toContain( + 'setProps(...) is deprecated in plain JavaScript React classes' + ); + expect(console.error.calls[4].args[0]).toContain( + 'replaceProps(...) is deprecated in plain JavaScript React classes' + ); }); it('supports this.context passed via getChildContext', function() { diff --git a/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts b/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts index 476a77ebd3..a92eefda9a 100644 --- a/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts +++ b/src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts @@ -492,7 +492,9 @@ describe('ReactTypeScriptClass', function() { expect(() => instance.getDOMNode()).toThrow(); expect(() => instance.replaceState({})).toThrow(); expect(() => instance.isMounted()).toThrow(); - expect((console.error).argsForCall.length).toBe(3); + expect(() => instance.setProps({ name: 'bar' })).toThrow(); + expect(() => instance.replaceProps({ name: 'bar' })).toThrow(); + expect((console.error).argsForCall.length).toBe(5); expect((console.error).argsForCall[0][0]).toContain( 'getDOMNode(...) is deprecated in plain JavaScript React classes' ); @@ -502,6 +504,12 @@ describe('ReactTypeScriptClass', function() { expect((console.error).argsForCall[2][0]).toContain( 'isMounted(...) is deprecated in plain JavaScript React classes' ); + expect((console.error).argsForCall[3][0]).toContain( + 'setProps(...) is deprecated in plain JavaScript React classes' + ); + expect((console.error).argsForCall[4][0]).toContain( + 'replaceProps(...) is deprecated in plain JavaScript React classes' + ); }); it('supports this.context passed via getChildContext', function() { diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 4b5aa4f591..9f3af5cfc0 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -30,6 +30,7 @@ var ReactDOMTextarea = require('ReactDOMTextarea'); var ReactMount = require('ReactMount'); var ReactMultiChild = require('ReactMultiChild'); var ReactPerf = require('ReactPerf'); +var ReactUpdateQueue = require('ReactUpdateQueue'); var assign = require('Object.assign'); var escapeTextContentForBrowser = require('escapeTextContentForBrowser'); @@ -129,6 +130,44 @@ function legacySetStateEtc() { } } +function legacySetProps(partialProps, callback) { + var component = this._reactInternalComponent; + if (__DEV__) { + warning( + false, + 'ReactDOMComponent: Do not access .setProps() of a DOM node. ' + + 'Instead, call React.render again at the top level.%s', + getDeclarationErrorAddendum(component) + ); + } + if (!component) { + return; + } + ReactUpdateQueue.enqueueSetPropsInternal(component, partialProps); + if (callback) { + ReactUpdateQueue.enqueueCallbackInternal(component, callback); + } +} + +function legacyReplaceProps(partialProps, callback) { + var component = this._reactInternalComponent; + if (__DEV__) { + warning( + false, + 'ReactDOMComponent: Do not access .replaceProps() of a DOM node. ' + + 'Instead, call React.render again at the top level.%s', + getDeclarationErrorAddendum(component) + ); + } + if (!component) { + return; + } + ReactUpdateQueue.enqueueReplacePropsInternal(component, partialProps); + if (callback) { + ReactUpdateQueue.enqueueCallbackInternal(component, callback); + } +} + var styleMutationWarning = {}; function checkAndWarnForMutatedStyle(style1, style2, component) { @@ -919,6 +958,8 @@ ReactDOMComponent.Mixin = { node.setState = legacySetStateEtc; node.replaceState = legacySetStateEtc; node.forceUpdate = legacySetStateEtc; + node.setProps = legacySetProps; + node.replaceProps = legacyReplaceProps; if (__DEV__) { if (canDefineProperty) { diff --git a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js index 55b0cb3229..d658a86237 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js @@ -974,6 +974,29 @@ describe('ReactDOMComponent', function() { expect(console.error.calls[4].args[0]).toContain('isMounted'); }); + it('handles legacy setProps and replaceProps', function() { + spyOn(console, 'error'); + var node = ReactTestUtils.renderIntoDocument(
rhinoceros
); + + node.setProps({className: 'herbiverous'}); + expect(node.className).toBe('herbiverous'); + expect(node.textContent).toBe('rhinoceros'); + + node.replaceProps({className: 'invisible rhino'}); + expect(node.className).toBe('invisible rhino'); + expect(node.textContent).toBe(''); + + expect(console.error.calls.length).toBe(2); + expect(console.error.calls[0].args[0]).toBe( + 'Warning: ReactDOMComponent: Do not access .setProps() of a DOM node. ' + + 'Instead, call React.render again at the top level.' + ); + expect(console.error.calls[1].args[0]).toBe( + 'Warning: ReactDOMComponent: Do not access .replaceProps() of a DOM ' + + 'node. Instead, call React.render again at the top level.' + ); + }); + it('does not touch ref-less nodes', function() { var node = ReactTestUtils.renderIntoDocument(
); expect(typeof node.getDOMNode).toBe('function'); diff --git a/src/renderers/shared/reconciler/ReactUpdateQueue.js b/src/renderers/shared/reconciler/ReactUpdateQueue.js index b4adf37e34..d406fe20df 100644 --- a/src/renderers/shared/reconciler/ReactUpdateQueue.js +++ b/src/renderers/shared/reconciler/ReactUpdateQueue.js @@ -12,9 +12,11 @@ 'use strict'; var ReactCurrentOwner = require('ReactCurrentOwner'); +var ReactElement = require('ReactElement'); var ReactInstanceMap = require('ReactInstanceMap'); var ReactUpdates = require('ReactUpdates'); +var assign = require('Object.assign'); var invariant = require('invariant'); var warning = require('warning'); @@ -106,7 +108,7 @@ var ReactUpdateQueue = { enqueueCallback: function(publicInstance, callback) { invariant( typeof callback === 'function', - 'enqueueCallback(...): You called ' + + 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn\'t callable.' ); @@ -136,7 +138,7 @@ var ReactUpdateQueue = { enqueueCallbackInternal: function(internalInstance, callback) { invariant( typeof callback === 'function', - 'enqueueCallback(...): You called ' + + 'enqueueCallback(...): You called `setProps`, `replaceProps`, ' + '`setState`, `replaceState`, or `forceUpdate` with a callback that ' + 'isn\'t callable.' ); @@ -231,6 +233,91 @@ var ReactUpdateQueue = { enqueueUpdate(internalInstance); }, + /** + * Sets a subset of the props. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {object} partialProps Subset of the next props. + * @internal + */ + enqueueSetProps: function(publicInstance, partialProps) { + var internalInstance = getInternalInstanceReadyForUpdate( + publicInstance, + 'setProps' + ); + if (!internalInstance) { + return; + } + ReactUpdateQueue.enqueueSetPropsInternal(internalInstance, partialProps); + }, + + enqueueSetPropsInternal: function(internalInstance, partialProps) { + var topLevelWrapper = internalInstance._topLevelWrapper; + invariant( + topLevelWrapper, + 'setProps(...): You called `setProps` on a ' + + 'component with a parent. This is an anti-pattern since props will ' + + 'get reactively updated when rendered. Instead, change the owner\'s ' + + '`render` method to pass the correct value as props to the component ' + + 'where it is created.' + ); + + // Merge with the pending element if it exists, otherwise with existing + // element props. + var wrapElement = topLevelWrapper._pendingElement || + topLevelWrapper._currentElement; + var element = wrapElement.props; + var props = assign({}, element.props, partialProps); + topLevelWrapper._pendingElement = ReactElement.cloneAndReplaceProps( + wrapElement, + ReactElement.cloneAndReplaceProps(element, props) + ); + + enqueueUpdate(topLevelWrapper); + }, + + /** + * Replaces all of the props. + * + * @param {ReactClass} publicInstance The instance that should rerender. + * @param {object} props New props. + * @internal + */ + enqueueReplaceProps: function(publicInstance, props) { + var internalInstance = getInternalInstanceReadyForUpdate( + publicInstance, + 'replaceProps' + ); + if (!internalInstance) { + return; + } + ReactUpdateQueue.enqueueReplacePropsInternal(internalInstance, props); + }, + + enqueueReplacePropsInternal: function(internalInstance, props) { + var topLevelWrapper = internalInstance._topLevelWrapper; + invariant( + topLevelWrapper, + 'replaceProps(...): You called `replaceProps` on a ' + + 'component with a parent. This is an anti-pattern since props will ' + + 'get reactively updated when rendered. Instead, change the owner\'s ' + + '`render` method to pass the correct value as props to the component ' + + 'where it is created.' + ); + + // Merge with the pending element if it exists, otherwise with existing + // element props. + var wrapElement = topLevelWrapper._pendingElement || + topLevelWrapper._currentElement; + var element = wrapElement.props; + topLevelWrapper._pendingElement = ReactElement.cloneAndReplaceProps( + wrapElement, + ReactElement.cloneAndReplaceProps(element, props) + ); + + enqueueUpdate(topLevelWrapper); + }, + enqueueElementInternal: function(internalInstance, newElement) { internalInstance._pendingElement = newElement; enqueueUpdate(internalInstance); diff --git a/src/renderers/shared/reconciler/ReactUpdates.js b/src/renderers/shared/reconciler/ReactUpdates.js index 0a92535e8f..293645e0a8 100644 --- a/src/renderers/shared/reconciler/ReactUpdates.js +++ b/src/renderers/shared/reconciler/ReactUpdates.js @@ -199,7 +199,7 @@ function enqueueUpdate(component) { // Various parts of our code (such as ReactCompositeComponent's // _renderValidatedComponent) assume that calls to render aren't nested; // verify that that's the case. (This is called by each top-level update - // function, like setState, forceUpdate, etc.; creation and + // function, like setProps, setState, forceUpdate, etc.; creation and // destruction of top-level components is guarded in ReactMount.) if (!batchingStrategy.isBatchingUpdates) { diff --git a/src/renderers/shared/reconciler/__tests__/ReactComponentLifeCycle-test.js b/src/renderers/shared/reconciler/__tests__/ReactComponentLifeCycle-test.js index 37f9d78da0..0a5a6b2993 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactComponentLifeCycle-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactComponentLifeCycle-test.js @@ -442,6 +442,46 @@ describe('ReactComponentLifeCycle', function() { expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE); }); + it('should throw when calling setProps() on an owned component', function() { + /** + * calls setProps in an componentDidMount. + */ + var Inner = React.createClass({ + render: function() { + return
; + }, + }); + var PropsUpdaterInOnDOMReady = React.createClass({ + componentDidMount: function() { + this.refs.theSimpleComponent.setProps({ + className: this.props.valueToUseInOnDOMReady, + }); + }, + render: function() { + return ( + + ); + }, + }); + var instance = + ; + expect(function() { + instance = ReactTestUtils.renderIntoDocument(instance); + }).toThrow( + 'Invariant Violation: setProps(...): You called `setProps` on a ' + + 'component with a parent. This is an anti-pattern since props will get ' + + 'reactively updated when rendered. Instead, change the owner\'s ' + + '`render` method to pass the correct value as props to the component ' + + 'where it is created.' + ); + }); + it('should not throw when updating an auxiliary component', function() { var Tooltip = React.createClass({ render: function() { @@ -472,18 +512,24 @@ describe('ReactComponentLifeCycle', function() { }, }); - var container = document.createElement('div'); - React.render( - , - container + var instance = ReactTestUtils.renderIntoDocument( + ); // Since `instance` is a root component, we can set its props. This also // makes Tooltip rerender the tooltip component, which shouldn't throw. - React.render( - , - container - ); + instance.setProps({text: 'dos', tooltipText: 'two'}); + }); + + it('should not allow setProps() called on an unmounted element', + function() { + var PropsToUpdate = React.createClass({ + render: function() { + return
; + }, + }); + var instance = ; + expect(instance.setProps).not.toBeDefined(); }); it('should allow state updates in componentDidMount', function() { @@ -549,7 +595,7 @@ describe('ReactComponentLifeCycle', function() { var container = document.createElement('div'); log = []; - React.render(, container); + var instance = React.render(, container); expect(log).toEqual([ 'outer componentWillMount', 'inner componentWillMount', @@ -558,7 +604,7 @@ describe('ReactComponentLifeCycle', function() { ]); log = []; - React.render(, container); + instance.setProps({x: 42}); expect(log).toEqual([ 'outer componentWillReceiveProps', 'outer shouldComponentUpdate', diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js index cbae94cf97..e30858b648 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js @@ -138,7 +138,10 @@ describe('ReactCompositeComponent', function() { it('should not cache old DOM nodes when switching constructors', function() { var instance = ; instance = ReactTestUtils.renderIntoDocument(instance); + instance.setProps({anchorClassOn: true}); // Warm any cache + instance.setProps({renderAnchor: false}); // Clear out the anchor // rerender + instance.setProps({renderAnchor: true, anchorClassOn: false}); var anchor = instance.getAnchor(); var actualDOMAnchorNode = React.findDOMNode(anchor); expect(actualDOMAnchorNode.className).toBe(''); @@ -396,6 +399,72 @@ describe('ReactCompositeComponent', function() { expect(instance2.state.value).toBe(1); }); + it('should not allow `setProps` on unmounted components', function() { + var container = document.createElement('div'); + document.body.appendChild(container); + + var Component = React.createClass({ + render: function() { + return
; + }, + }); + + var instance = ; + expect(instance.setProps).not.toBeDefined(); + + instance = React.render(instance, container); + expect(function() { + instance.setProps({value: 1}); + }).not.toThrow(); + expect(console.error.calls.length).toBe(0); + + React.unmountComponentAtNode(container); + expect(function() { + instance.setProps({value: 2}); + }).not.toThrow(); + + expect(console.error.calls.length).toBe(1); + expect(console.error.argsForCall[0][0]).toBe( + 'Warning: setProps(...): Can only update a mounted or ' + + 'mounting component. This usually means you called setProps() on an ' + + 'unmounted component. This is a no-op. Please check the code for the ' + + 'Component component.' + ); + }); + + it('should only allow `setProps` on top-level components', function() { + var container = document.createElement('div'); + document.body.appendChild(container); + + var innerInstance; + + var Inner = React.createClass({ + render: function() { + return
; + }, + }); + var Component = React.createClass({ + render: function() { + return
; + }, + componentDidMount: function() { + innerInstance = this.refs.inner; + }, + }); + React.render(, container); + + expect(innerInstance).not.toBe(undefined); + expect(function() { + innerInstance.setProps({value: 1}); + }).toThrow( + 'Invariant Violation: setProps(...): You called `setProps` on a ' + + 'component with a parent. This is an anti-pattern since props will get ' + + 'reactively updated when rendered. Instead, change the owner\'s ' + + '`render` method to pass the correct value as props to the component ' + + 'where it is created.' + ); + }); + it('should cleanup even if render() fatals', function() { var BadComponent = React.createClass({ render: function() { @@ -866,8 +935,7 @@ describe('ReactCompositeComponent', function() { }, }); - var container = document.createElement('div'); - var comp = React.render(, container); + var comp = ReactTestUtils.renderIntoDocument(); expect(React.findDOMNode(comp.refs.static0).textContent).toBe('A'); expect(React.findDOMNode(comp.refs.static1).textContent).toBe('B'); @@ -878,7 +946,7 @@ describe('ReactCompositeComponent', function() { // When flipping the order, the refs should update even though the actual // contents do not - React.render(, container); + comp.setProps({flipped: true}); expect(React.findDOMNode(comp.refs.static0).textContent).toBe('B'); expect(React.findDOMNode(comp.refs.static1).textContent).toBe('A'); diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponentState-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponentState-test.js index fda4de2cec..1547f47f33 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponentState-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponentState-test.js @@ -138,9 +138,8 @@ describe('ReactCompositeComponent-state', function() { this.peekAtState('initial-callback'); } ); - React.render( - , - container, + instance.setProps( + {nextColor: 'green'}, instance.peekAtCallback('setProps') ); instance.setFavoriteColor('blue'); diff --git a/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js b/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js index 61faf39897..4e54164c13 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js @@ -15,6 +15,7 @@ require('mock-modules'); var React = require('React'); var ReactInstanceMap = require('ReactInstanceMap'); +var ReactTestUtils = require('ReactTestUtils'); var ReactMount = require('ReactMount'); var mapObject = require('mapObject'); @@ -211,20 +212,15 @@ function verifyDomOrderingAccurate(parentInstance, statusDisplays) { */ function testPropsSequence(sequence) { var i; - var container = document.createElement('div'); - var parentInstance = React.render( - , - container + var parentInstance = ReactTestUtils.renderIntoDocument( + ); var statusDisplays = parentInstance.getStatusDisplays(); var lastInternalStates = getInteralStateByUserName(statusDisplays); verifyStatuses(statusDisplays, sequence[0]); for (i = 1; i < sequence.length; i++) { - React.render( - , - container - ); + parentInstance.replaceProps(sequence[i]); statusDisplays = parentInstance.getStatusDisplays(); verifyStatuses(statusDisplays, sequence[i]); verifyStatesPreserved(lastInternalStates, statusDisplays); @@ -247,27 +243,19 @@ describe('ReactMultiChildReconcile', function() { }, }; - var container = document.createElement('div'); - var parentInstance = React.render( - , - container + var parentInstance = ReactTestUtils.renderIntoDocument( + ); var statusDisplays = parentInstance.getStatusDisplays(); var startingInternalState = statusDisplays.jcw.getInternalState(); // Now remove the child. - React.render( - , - container - ); + parentInstance.replaceProps({usernameToStatus: {} }); statusDisplays = parentInstance.getStatusDisplays(); expect(statusDisplays.jcw).toBeFalsy(); // Now reset the props that cause there to be a child - React.render( - , - container - ); + parentInstance.replaceProps(props); statusDisplays = parentInstance.getStatusDisplays(); expect(statusDisplays.jcw).toBeTruthy(); expect(statusDisplays.jcw.getInternalState()) diff --git a/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js b/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js index a2066d879a..10fa281ac2 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactUpdates-test.js @@ -100,12 +100,14 @@ describe('ReactUpdates', function() { expect(instance.state.y).toBe(0); ReactUpdates.batchedUpdates(function() { + instance.setProps({x: 1}); instance.setState({y: 2}); expect(instance.props.x).toBe(0); expect(instance.state.y).toBe(0); expect(updateCount).toBe(0); }); + expect(instance.props.x).toBe(1); expect(instance.state.y).toBe(2); expect(updateCount).toBe(1); });