Enzyme: wrapper.instance() throws error after wrapper.unmount()

Created on 25 May 2017  ยท  6Comments  ยท  Source: enzymejs/enzyme

When calling wrapper.instance() after wrapper.unmount() it throws TypeError: component.getPublicInstance is not a function

The use-case for accessing the React component instance after the unmount is to check that any possible teardown in componentWillUnmount() has been successful. In my particular use-case I want to check that an RxJS Subscription has been unsubscribed.

Code to reproduce:

import React from 'react';
import Rx from 'rxjs';
import { mount } from 'enzyme';

class MyComponent extends React.Component {

  constructor() {
    super();
    this.state = {
      foo: 'initial'
    };
  }

  componentDidMount() {
    this.subscription = this.props.subject.subscribe(
      value => this.setState({ foo: value })
    );
  }

  componentWillUnmount() {
    this.subscription.unsubscribe();
  }

  render() {
    return <div>{ this.state.foo }</div>;
  }
}

it('unsubscribes on unmount', () => {
  const subject = new Rx.BehaviorSubject('new value');

  const wrapper = mount(
    <MyComponent subject={subject} />,
  );
  expect(wrapper.instance()).toHaveProperty('subscription.closed', false);
  wrapper.unmount();
  expect(wrapper.instance()).toHaveProperty('subscription.closed', true);
});

Test output:

FAIL  src/ui/memo/__tests__/AfterUnmount.test.js
  โ— unsubscribes on unmount

    TypeError: component.getPublicInstance is not a function

      at WrapperComponent.getInstance (node_modules/enzyme/build/ReactWrapperComponent.js:81:32)
      at ReactWrapper.instance (node_modules/enzyme/build/ReactWrapper.js:219:31)
      at Object.<anonymous>.it (src/ui/memo/__tests__/AfterUnmount.test.js:37:18)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
      at process._tickCallback (internal/process/next_tick.js:103:7)

There is a workaround by using wrapper.node instead of wrapper.instance() in the last expect: expect(wrapper.node).toHaveProperty('subscription.closed', true);. But this feels dirty because .node is an undocumented feature.

Used versions:
enzyme: 2.8.2
jest: 20.0.3
react: 15.5.4
react-scripts: 1.0.5
rxjs: 5.4.0
enzyme-to-json: 1.5.1 (I don't think this affects this particular test, but I had it installed anyway)

mount bug help wanted

Most helpful comment

const instance = wrapper.instance(); and then refer to instance after that?

All 6 comments

const instance = wrapper.instance(); and then refer to instance after that?

Yeah, that's a nice workaround, thanks!

I think it's still a bug, though. I don't know the internals, but clearly the instance still exists after unmount() because we can access it via reference. So, it's unintuitive that we can't access it via the instance().

This could be either fixed so that instance() still works after unmount() or at least make the documentation more clear when you can and when you cannot call instance().

Fair points.

I think it's worth ensuring instance still works after unmount.

Does this only happen on mount, or also on shallow?

With shallow() it works fine :+1:

Here is still a minimal snippet to reproduce the original problem with mount():

const wrapper = mount(<div></div>);
wrapper.unmount();
wrapper.instance();

I could try to take a crack at this.

I took a shot at this in #969

The issue was that unmount causes the ReactWrapperComponent render to return null which renders an empty component. Apparently empty components don't have the .getPublicInstance() method that was being used in .getInstance() so it threw a TypeError. I figured the easiest/least-invasive way to still get at it was to store a reference to the last render on the components state.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

heikkimu picture heikkimu  ยท  3Comments

timhonders picture timhonders  ยท  3Comments

potapovDim picture potapovDim  ยท  3Comments

modemuser picture modemuser  ยท  3Comments

ahuth picture ahuth  ยท  3Comments