diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
index 9aa5a782a..1caff92d6 100644
--- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
+++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
@@ -2406,6 +2406,86 @@ describe('shallow', () => {
});
});
+ describe('should not call componentWillReceiveProps after setState is called', () => {
+ it('should not call componentWillReceiveProps upon rerender', () => {
+ class A extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.state = { a: 0 };
+ }
+
+ componentWillReceiveProps() {
+ this.setState({ a: 1 });
+ }
+
+ render() {
+ return (
{this.state.a}
);
+ }
+ }
+ const spy = sinon.spy(A.prototype, 'componentWillReceiveProps');
+
+ const wrapper = shallow(, { disableLifecycleMethods: true });
+
+ wrapper.setState({ a: 2 });
+ expect(wrapper.state('a')).to.equal(2);
+
+ expect(spy).to.have.property('callCount', 0);
+ wrapper.setProps({});
+ expect(spy).to.have.property('callCount', 1);
+ expect(wrapper.state('a')).to.equal(1);
+
+ return new Promise((resolve) => {
+ wrapper.setState({ a: 3 }, resolve);
+ }).then(() => {
+ expect(spy).to.have.property('callCount', 1);
+ expect(wrapper.state('a')).to.equal(3);
+ });
+ });
+
+ it('should not call componentWillReceiveProps with multiple keys in props', () => {
+ class B extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { a: 0, b: 1 };
+ }
+
+ componentWillReceiveProps() {
+ this.setState({ b: 0, a: 1 });
+ }
+
+ render() {
+ return (
+
+ {this.state.a + this.state.b}
+
+ );
+ }
+ }
+ const spy = sinon.spy(B.prototype, 'componentWillReceiveProps');
+
+ const wrapper = shallow(, { disableLifecycleMethods: true });
+
+ wrapper.setState({ a: 2 });
+ expect(wrapper.state('a')).to.equal(2);
+ expect(wrapper.state('b')).to.equal(1);
+
+ expect(spy).to.have.property('callCount', 0);
+ wrapper.setProps({});
+ expect(spy).to.have.property('callCount', 1);
+ expect(wrapper.state('a')).to.equal(1);
+
+ return Promise.all([
+ new Promise((resolve) => { wrapper.setState({ b: 5 }, resolve); }),
+ new Promise((resolve) => { wrapper.setState({ a: 10 }, resolve); }),
+ ]).then(() => {
+ expect(spy).to.have.property('callCount', 1);
+ expect(wrapper.state('b')).to.equal(5);
+ expect(wrapper.state('a')).to.equal(10);
+ });
+ });
+ });
+
describeIf(is('> 0.13'), 'stateless function components', () => {
it('should throw when trying to access state', () => {
const Foo = () => (
diff --git a/packages/enzyme/src/ShallowWrapper.js b/packages/enzyme/src/ShallowWrapper.js
index 47ae8465b..264a54597 100644
--- a/packages/enzyme/src/ShallowWrapper.js
+++ b/packages/enzyme/src/ShallowWrapper.js
@@ -1,4 +1,5 @@
import flat from 'array.prototype.flat';
+import isEqual from 'lodash.isequal';
import cheerio from 'cheerio';
import {
@@ -374,7 +375,7 @@ class ShallowWrapper {
}
}
// If it doesn't need to rerender, update only its props.
- } else if (props) {
+ } else if (!isEqual(props, instance.props)) {
instance.props = (Object.freeze || Object)({ ...instance.props, ...props });
}
this.update();