Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setState callback is not called (React 16?) #953

Closed
rroehrig opened this issue May 24, 2017 · 12 comments
Closed

setState callback is not called (React 16?) #953

rroehrig opened this issue May 24, 2017 · 12 comments

Comments

@rroehrig
Copy link

rroehrig commented May 24, 2017

In short:
When testing a component which depends on the callback of setState(updater, callback), it's not working since our last react-native/react upgrade.

In detail:
Since the last react-native upgrade (which comes with react 16 alpha), a test is failing which previously worked. I report this issue here because the production code works. setState(updater, callback) has been changed in react 16, so I assume some enzyme "glue code" does not work anymore.
I've created a snippet which shows the problem:

import React, { Component } from 'react';
import { shallow } from 'enzyme';
import { TextInput } from 'react-native';

class MyTextInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
            text: props.text,
            // another prop for another TextInput...
        };
    }

    render() {
        return (
            <TextInput
                onChangeText={(text) => {
                    this.setState({ text }, () => this.props.onChangeText(this.state));
                }}
                value={this.state.text}
            />
            // another TextInput...
        );
    }
}

describe('MyTextInput', () => {
    it('shows the bug', () => {
        const onChangeMock = jest.fn();
        const textField = shallow(<MyTextInput text={'41'} onChangeText={onChangeMock} />);
        textField.simulate('ChangeText', '42');
        expect(textField.state('text')).toBe('42'); // OK - setState's updater works
        expect(onChangeMock).toHaveBeenCalledTimes(1); // NOT OK - setState's callback is not working
        expect(onChangeMock).toHaveBeenCalledWith({ text: '42' }); // NOT OK
    });
});

Relevant dependencies:

  • enzyme@2.8.2
  • react@16.0.0-alpha.6
  • react-native@0.44.0

Thank you in advance!

@ljharb
Copy link
Member

ljharb commented May 24, 2017

Enzyme is not currently compatible with react 16 alpha, which also means it's not compatible with a version of RN that requires it.

@ljharb ljharb closed this as completed May 24, 2017
@AlbertBrand
Copy link

@rroehrig Update your react-test-renderer as well to @16.0.0-alpha.6 and see if the issue still exists.

@rroehrig
Copy link
Author

@AlbertBrand Thanks for the hint! I tried to update this dependency as well but it's still not working.

@AlbertBrand
Copy link

Ah, makes sense as you are using Enzyme. I had the same issue but I'm only using the test renderer via Jest. Enzyme probably has its own implementation.

@benadamstyles
Copy link

I'm hitting this issue as well but I'm not using enzyme, just react-test-renderer@16.0.0-alpha.12. @AlbertBrand did you manage to resolve this? Thanks for your help!

@benadamstyles
Copy link

benadamstyles commented Jul 1, 2017

I think I have found the issue. react-test-renderer-shallow.development.js has the following:

Updater.prototype.enqueueSetState = function enqueueSetState(publicInstance, partialState, callback, callerName) {
  if (typeof partialState === 'function') {
    partialState = partialState(publicInstance.state, publicInstance.props);
  }

  this._renderer._newState = _extends({}, publicInstance.state, partialState);

  this._renderer.render(this._renderer._element, this._renderer._context);
};

As you can see, the callback argument is simply ignored, so it is never called. I'm going to try using the full renderer rather than the shallow one and see if it works that way, as react-test-renderer.development.js has the following:

enqueueSetState: function (instance, partialState, callback) {
  var fiber = ReactInstanceMap_1.get(instance);
  var priorityLevel = getPriorityContext(fiber, false);
  callback = callback === undefined ? null : callback;
  {
    warnOnInvalidCallback(callback, 'setState');
  }
  addUpdate$1(fiber, partialState, callback, priorityLevel);
  scheduleUpdate(fiber, priorityLevel);
}

where the callback argument is used.

UPDATE: It works! Use the following instead of the shallow renderer, and the setState callback is called:

import renderer from 'react-test-renderer'
const deepInstance = renderer.create(<YourComponent />).getInstance()

@mabounassif
Copy link

No solution with enzyme? Using react-test-renderer would completely change the test setup.

@StefanoSega
Copy link

with:

"jest": "^23.2.0",
"babel-jest": "^23.2.0",
"enzyme": "^3.3.0",
"react-test-renderer": "^16.4.1",

it doesn't work, setState callback is somehow ignored

@ljharb
Copy link
Member

ljharb commented Jul 2, 2018

@StefanoSega thanks, would you mind filing a new issue about that?

@Soheevich

This comment has been minimized.

@ljharb

This comment has been minimized.

@Soheevich

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants