diff --git a/packages/react-test-renderer/src/ReactShallowRenderer.js b/packages/react-test-renderer/src/ReactShallowRenderer.js index fc3baf251f029..8f64bef8e0da6 100644 --- a/packages/react-test-renderer/src/ReactShallowRenderer.js +++ b/packages/react-test-renderer/src/ReactShallowRenderer.js @@ -11,6 +11,7 @@ import describeComponentFrame from 'shared/describeComponentFrame'; import getComponentName from 'shared/getComponentName'; import emptyObject from 'fbjs/lib/emptyObject'; import invariant from 'fbjs/lib/invariant'; +import shallowEqual from 'fbjs/lib/shallowEqual'; import checkPropTypes from 'prop-types/checkPropTypes'; class ReactShallowRenderer { @@ -63,7 +64,7 @@ class ReactShallowRenderer { this._context = context; if (this._instance) { - this._updateClassComponent(element.props, context); + this._updateClassComponent(element.type, element.props, context); } else { if (shouldConstruct(element.type)) { this._instance = new element.type( @@ -133,7 +134,8 @@ class ReactShallowRenderer { // because DOM refs are not available. } - _updateClassComponent(props, context) { + _updateClassComponent(type, props, context) { + const oldState = this._instance.state || emptyObject; const oldProps = this._instance.props; if ( @@ -145,33 +147,40 @@ class ReactShallowRenderer { // Read state after cWRP in case it calls setState // Fallback to previous instance state to support rendering React.cloneElement() + // TODO: the cloneElement() use case is broken and should be removed + // https://github.com/facebook/react/issues/11441 const state = this._newState || this._instance.state || emptyObject; - if ( - typeof this._instance.shouldComponentUpdate === 'function' && - !this._forcedUpdate - ) { - if ( - this._instance.shouldComponentUpdate(props, state, context) === false - ) { - this._instance.context = context; - this._instance.props = props; - this._instance.state = state; - this._forcedUpdate = false; - - return; - } + let shouldUpdate = true; + if (this._forcedUpdate) { + shouldUpdate = true; + this._forcedUpdate = false; + } else if (typeof this._instance.shouldComponentUpdate === 'function') { + shouldUpdate = !!this._instance.shouldComponentUpdate( + props, + state, + context, + ); + } else if (type && type.prototype && type.prototype.isPureReactComponent) { + // TODO: we can remove the type existence check when we fix this: + // https://github.com/facebook/react/issues/11441 + shouldUpdate = + !shallowEqual(oldProps, props) || !shallowEqual(oldState, state); } - if (typeof this._instance.componentWillUpdate === 'function') { - this._instance.componentWillUpdate(props, state, context); + if (shouldUpdate) { + if (typeof this._instance.componentWillUpdate === 'function') { + this._instance.componentWillUpdate(props, state, context); + } } this._instance.context = context; this._instance.props = props; this._instance.state = state; - this._rendered = this._instance.render(); + if (shouldUpdate) { + this._rendered = this._instance.render(); + } // Intentionally do not call componentDidUpdate() // because DOM refs are not available. } diff --git a/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js b/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js index 1f51e390040b6..7bd5cf159cff6 100644 --- a/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js +++ b/packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js @@ -122,24 +122,64 @@ describe('ReactShallowRenderer', () => { expect(shallowRenderer.getRenderOutput()).toEqual(