Skip to content

Commit

Permalink
[Fix] shallow: do not call cDM unexpectedly (#1768)
Browse files Browse the repository at this point in the history
Fixes #1765. Fixes #1766.
  • Loading branch information
koba04 authored and ljharb committed Aug 17, 2018
1 parent a1b51b4 commit b3fae62
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 19 deletions.
30 changes: 30 additions & 0 deletions packages/enzyme-test-suite/test/ReactWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5111,5 +5111,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 (
<div>
<button onClick={() => this.setState({ foo: 'update2' })}>
click
</button>
{this.state.foo}
</div>
);
}
}
const spy = sinon.spy(Foo.prototype, 'componentDidMount');

const wrapper = mount(<Foo />);
expect(spy).to.have.property('callCount', 1);
wrapper.find('button').prop('onClick')();
expect(spy).to.have.property('callCount', 1);
});
});
});
60 changes: 60 additions & 0 deletions packages/enzyme-test-suite/test/ShallowWrapper-spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4921,6 +4921,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 (
<div>
<button onClick={() => this.setState({ foo: 'update2' })}>
click
</button>
{this.state.foo}
</div>
);
}
}
const spy = sinon.spy(Foo.prototype, 'componentDidMount');

const wrapper = shallow(<Foo />);
expect(spy).to.have.property('callCount', 1);
wrapper.find('button').prop('onClick')();
expect(spy).to.have.property('callCount', 1);
});
});

describeIf(is('>= 16'), 'support getSnapshotBeforeUpdate', () => {
Expand Down Expand Up @@ -4981,6 +5011,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 (<table />);
}
}

class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
showTable: false,
};
}

componentDidMount() {
this.setState({ showTable: true });
}

render() {
const { showTable } = this.state;
return (<div>{showTable ? <Table /> : null}</div>);
}
}
const wrapper = shallow(<MyComponent />, { 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 {
Expand Down
39 changes: 20 additions & 19 deletions packages/enzyme/src/ShallowWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
}

Expand Down

0 comments on commit b3fae62

Please sign in to comment.