enzyme: `.setProps()` doesn't trigger componentDidUpdate lifecycle method

When using shallow, the .setProps() method doesn’t trigger the componentDidUpdate lifecycle method. However, .setState() does trigger it. It seems like they both should (or both shouldn’t).

simple example
describe('MyComponent', () => {
  let spy;

  before(function() {
    spy = sinon.spy(MyComponent.prototype, 'componentDidUpdate');
  });

  after(function() {
    spy.restore();
  });

  // fails
  it('calls componentDidUpdate when setting props', () => {
    const wrapper = shallow(<MyComponent color="yellow" />);
    wrapper.setProps({ 'color': 'green' });
    expect(spy.calledOnce).to.be.true;
  });

  // passes
  it('calls componentDidUpdate when setting state', () => {
    const wrapper = shallow(<MyComponent color="yellow" />);
    wrapper.setState({ foo: 'bar' });
    expect(spy.calledOnce).to.be.true;
  });
});

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 12
  • Comments: 30 (11 by maintainers)

Most upvoted comments

@bricejlin #318 is hidden in the lifecycleExperimental flag. You can do with the flag for enabling all lifecycle methods in shallow.

shallow(<Foo />, { lifecycleExperimental: true })

At the moment I’m doing

const prevProp = wrapper.props()
wrapper.setProps({
  current_step: 2,
})
// forces an update call to componentDidUpdate - https://github.com/airbnb/enzyme/issues/34
wrapper.instance().componentDidUpdate(prevProp)

The latest update seems to broke this again. node_modules/enzyme/build/ShallowWrapper.js line 552

   if (lifecycles.componentDidUpdate && typeof instance.componentDidUpdate === 'function' && (!state || (0, _Utils.shallowEqual)(state, _this3.instance().state))) {
                    instance.componentDidUpdate(prevProps, state, snapshot);
                  }

The last condition make it that only state change will trigger instance.componentUpdate,

Sorry to bring an old thread back up, but I’m running into a similar issue detailed here, and I can’t seem to find any other solutions when searching. Here’s my test:

  it('should reset gridWrapperNode scrollTop position if data has changed', () => {
    const mounted = mount(<Grid {...props} />);

    const gridNode = document.createElement('div');
    const gridWrapper = document.createElement('div');

    gridWrapper.classList.add('datagrid-grid-wrapper');
    gridNode.appendChild(gridWrapper);

    mounted.instance().gridNode = gridNode;

    gridWrapper.scrollTop = 40;
    mounted.instance().lastScrollTop = 40;

    mounted.setProps({
      ...props,
      data: [{ val: 'someNewData' }],
    });

    // mounted.instance().componentDidUpdate({});

    expect(gridWrapper.scrollTop).toEqual(0);
    expect(mounted.instance().lastScrollTop).toEqual(0);
  });

When I uncomment out // mounted.instance().componentDidUpdate({});, it works as expected, however I’m expecting the componentDidUpdate method to be called with the props changing. I checked, the props are updating as they should so I was wondering if this is a known issue or if it’s something I’m doing wrong?

In react-native lifecycleExperimental works for componentDidMount() but not for componentDidUpdate().

"name": "enzyme",
"version": "3.1.0",
"react": "16.0.0",
"react-native": "0.47.2",

https://github.com/airbnb/enzyme/issues/1279

Solved it by using

         wrapper.setProps({
            user: {
                test: {error: "test"}
            }
        });

        wrapper.find('.smth').simulate('click');

Works as a charm, setprops are set, you just need to call a function again that triggers the component o re-render, it will trigger the componentDidUpdate function

@blainekasten thanks for that info. Funny - I didn’t even know .setProps() existed as a real API.

The deprecation doesn’t affect us in this case, since .setProps() is achieved using setState() of a parent wrapper component, anyway.