Enzyme: `this` is undefined in bound functions when shallow rendering

Created on 16 Aug 2018  ·  16Comments  ·  Source: enzymejs/enzyme

Describe the bug
The following test passes in 3.4.1 but fails in 3.4.2.

class Comp extends Component {
  state = {
    key: "",
  }

  componentDidMount() {
    this.instanceFunction();
  }

  instanceFunction = () => this.setState(() => ({ key: "value" }));

  render() {
    const { key } = this.state;
    return null;
  }
}

it("should work", () => {
  shallow(<Comp />);
});

The error in 3.4.2 is:

FAIL src/test.js
  ✕ should work (16ms)

  ● should work

    Method “setState” is only meant to be run on a single node. undefined found instead.

      11 |   }
      12 |
    > 13 |   instanceFunction = () => this.setState(() => ({ key: "value" }));
         |                                 ^
      14 |
      15 |   render() {
      16 |     const { key } = this.state;

      at ShallowWrapper.single (node_modules/enzyme/build/ShallowWrapper.js:1718:17)
      at ShallowWrapper.setState (node_modules/enzyme/build/ShallowWrapper.js:499:14)
      at Comp.ShallowWrapper.instance.setState (node_modules/enzyme/build/ShallowWrapper.js:184:33)
      at Comp.setState [as instanceFunction] (src/test.js:13:33)
      at Comp.instanceFunction [as componentDidMount] (src/test.js:10:10)
      at node_modules/enzyme/build/ShallowWrapper.js:189:20
      at Object.batchedUpdates (node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:392:22)
      at new ShallowWrapper (node_modules/enzyme/build/ShallowWrapper.js:188:24)
      at shallow (node_modules/enzyme/build/shallow.js:21:10)
      at Object.<anonymous> (src/test.js:22:3)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.147s
Ran all test suites matching /src\/test.js/i.
error Command failed with exit code 1.

To Reproduce
Steps to reproduce the behavior:

  1. Run the above test with [email protected]

Expected behavior
The test should pass.

Desktop (please complete the following information):

  • OS: Mac OSX (but also fails in Windows)
  • Browser n/a
  • Version n/a
bug

Most helpful comment

+1 yes happening to us too, due to https://github.com/airbnb/enzyme/blob/a50570d847fac11dc122c43651107e46ba03de73/packages/enzyme/src/ShallowWrapper.js#L177-L180 as setState is getting appended to instance.
I think we should avoid addingsetState to instance as then also the code will get executed without appending instance.

All 16 comments

+1
Probably introduced here: https://github.com/airbnb/enzyme/commit/a50570d847fac11dc122c43651107e46ba03de73#diff-fa27c82ee4fde075bae6173848e17c82

+1 yes happening to us too, due to https://github.com/airbnb/enzyme/blob/a50570d847fac11dc122c43651107e46ba03de73/packages/enzyme/src/ShallowWrapper.js#L177-L180 as setState is getting appended to instance.
I think we should avoid addingsetState to instance as then also the code will get executed without appending instance.

+1 got this error this morning aswell.

+1 got this same issue after upgrade to 3.4.2

Any ETA on fixing this or backing out the change? I have a bunch of builds that are failing and would hate to have to go through each of them and pin to a version.

@deedubbu i'm working on it; i hope to have a patch out tonight.

Sorry for the regression. I’m going to write a patch for this.

@koba04 please review #1763

@ljharb Thank you! I'm reviewing it.

Please upgrade to v3.4.3 🙏

I believe this may still be an issue. In my shallow rendered component tests I'm getting:

Method “setState” is only meant to be run on a single node. undefined found instead.

componentDidMount(){
   this.setState({ valid: this.props.oldEmail != this.state.email })
}

Resolved by reverting to 3.4.1 for now....

Let me know if I can provide more info :)

@Tinusw Please open a new issue with a test case that reproduces it.

Looks like any change will trigger componentDidMount.

Not sure why, but the 3.4.3 release broke my tests. I receive Error: ShallowWrapper::setState() can only be called on the root errors all over the place.

The relevant test is attached. I was able to figure out that componentDidMount is called twice with the 3.4.3 release, which was not the case before.

  it('<MyComponent />', async () => {
    fetch.mockResponse(JSON.stringify(jsonData));
    const wrapper = shallow(<MyComponent />, { disableLifecycleMethods: true });
    await wrapper.instance().componentDidMount();
    wrapper.update();
    expect(fetch.mock.calls.length).toEqual(1);
    expect(wrapper.find(Table).length).toEqual(1);
  });

It's very helpful to open a new issue with a test case that reproduces it.

Sorry about that, I'm on it :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

blainekasten picture blainekasten  ·  3Comments

AdamYahid picture AdamYahid  ·  3Comments

modemuser picture modemuser  ·  3Comments

ivanbtrujillo picture ivanbtrujillo  ·  3Comments

andrewhl picture andrewhl  ·  3Comments