diff --git a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx index 60bcf82ff..8d0a8fe85 100644 --- a/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ReactWrapper-spec.jsx @@ -5196,5 +5196,35 @@ describeWithDOM('mount', () => { expect(spy).to.have.property('callCount', 1); expect(wrapper.state('foo')).to.equal('update'); }); + + it('should not call `componentDidMount` twice when a child component is created', () => { + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + foo: 'init', + }; + } + + componentDidMount() {} + + render() { + return ( +
+ + {this.state.foo} +
+ ); + } + } + const spy = sinon.spy(Foo.prototype, 'componentDidMount'); + + const wrapper = mount(); + expect(spy).to.have.property('callCount', 1); + wrapper.find('button').prop('onClick')(); + expect(spy).to.have.property('callCount', 1); + }); }); }); diff --git a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx index 2745b9faa..0d74d92bc 100644 --- a/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx +++ b/packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx @@ -4994,6 +4994,36 @@ describe('shallow', () => { expect(spy).to.have.property('callCount', 1); expect(wrapper.state('foo')).to.equal('update'); }); + + it('should not call `componentDidMount` twice when a child component is created', () => { + class Foo extends React.Component { + constructor(props) { + super(props); + this.state = { + foo: 'init', + }; + } + + componentDidMount() {} + + render() { + return ( +
+ + {this.state.foo} +
+ ); + } + } + const spy = sinon.spy(Foo.prototype, 'componentDidMount'); + + const wrapper = shallow(); + expect(spy).to.have.property('callCount', 1); + wrapper.find('button').prop('onClick')(); + expect(spy).to.have.property('callCount', 1); + }); }); describeIf(is('>= 16'), 'support getSnapshotBeforeUpdate', () => { @@ -5054,6 +5084,36 @@ describe('shallow', () => { expect(spy).to.have.property('callCount', 0); }); + it('should be able to call `componentDidMount` directly when disableLifecycleMethods is true', () => { + class Table extends React.Component { + render() { + return (); + } + } + + class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + showTable: false, + }; + } + + componentDidMount() { + this.setState({ showTable: true }); + } + + render() { + const { showTable } = this.state; + return (
{showTable ?
: null}); + } + } + const wrapper = shallow(, { disableLifecycleMethods: true }); + expect(wrapper.find(Table).length).to.equal(0); + wrapper.instance().componentDidMount(); + expect(wrapper.find(Table).length).to.equal(1); + }); + it('should call shouldComponentUpdate when disableLifecycleMethods flag is true', () => { const spy = sinon.spy(); class Foo extends React.Component { diff --git a/packages/enzyme/src/ShallowWrapper.js b/packages/enzyme/src/ShallowWrapper.js index e7f774755..ff1c5e40b 100644 --- a/packages/enzyme/src/ShallowWrapper.js +++ b/packages/enzyme/src/ShallowWrapper.js @@ -168,37 +168,38 @@ class ShallowWrapper { const adapter = getAdapter(options); const lifecycles = getAdapterLifecycles(adapter); - let renderedNode; + // mounting a ShallowRender component if (!root) { privateSet(this, ROOT, this); privateSet(this, UNRENDERED, nodes); const renderer = adapter.createRenderer({ mode: 'shallow', ...options }); privateSet(this, RENDERER, renderer); this[RENDERER].render(nodes, options.context); - renderedNode = this[RENDERER].getNode(); + const renderedNode = this[RENDERER].getNode(); privateSetNodes(this, getRootNode(renderedNode)); + privateSet(this, OPTIONS, options); + + const { instance } = renderedNode; + if (instance && !options.disableLifecycleMethods) { + // Ensure to call componentDidUpdate when instance.setState is called + if (lifecycles.componentDidUpdate.onSetState && !instance[SET_STATE]) { + privateSet(instance, SET_STATE, instance.setState); + instance.setState = (...args) => this.setState(...args); + } + + if (typeof instance.componentDidMount === 'function') { + this[RENDERER].batchedUpdates(() => { + instance.componentDidMount(); + }); + } + } + // creating a child component through enzyme's ShallowWrapper APIs. } else { privateSet(this, ROOT, root); privateSet(this, UNRENDERED, null); privateSet(this, RENDERER, root[RENDERER]); privateSetNodes(this, nodes); - renderedNode = this[RENDERER].getNode(); - } - privateSet(this, OPTIONS, root ? root[OPTIONS] : options); - - const { instance } = renderedNode; - if (instance && !options.disableLifecycleMethods) { - // Ensure to call componentDidUpdate when instance.setState is called - if (lifecycles.componentDidUpdate.onSetState && !instance[SET_STATE]) { - privateSet(instance, SET_STATE, instance.setState); - instance.setState = (...args) => this.setState(...args); - } - - if (typeof instance.componentDidMount === 'function') { - this[RENDERER].batchedUpdates(() => { - instance.componentDidMount(); - }); - } + privateSet(this, OPTIONS, root[OPTIONS]); } }