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();