diff --git a/src/diff/index.js b/src/diff/index.js index 478f807e8f..6adad6b2b5 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -132,7 +132,10 @@ export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChi c.base = newVNode._dom; - while (tmp=c._renderCallbacks.pop()) tmp.call(c); + while (tmp=c._renderCallbacks.pop()) { + if (c._nextState) { c.state = c._nextState; } + tmp.call(c); + } // Don't call componentDidUpdate on mount or when we bailed out via // `shouldComponentUpdate` diff --git a/test/browser/components.test.js b/test/browser/components.test.js index 396fd9e6b4..3db9e82975 100644 --- a/test/browser/components.test.js +++ b/test/browser/components.test.js @@ -132,6 +132,42 @@ describe('Components', () => { expect(spy).to.not.be.called; }); + it('should accurately call nested setState callbacks', () => { + let states = []; + let finalState; + class Foo extends Component { + constructor(props) { + super(props); + this.state = { a: 'b' }; + } + + componentDidMount() { + states.push(this.state); + // eslint-disable-next-line + this.setState({ a: 'a' }, () => { + states.push(this.state); + this.setState({ a: 'c' }, () => { + states.push(this.state); + }); + }); + } + + render() { + finalState = this.state; + return

Test

; + } + } + + render(, scratch); + rerender(); + + let [firstState, secondState, thirdState] = states; + expect(finalState).to.deep.equal({ a: 'c' }); + expect(firstState).to.deep.equal({ a: 'b' }); + expect(secondState).to.deep.equal({ a: 'a' }); + expect(thirdState).to.deep.equal({ a: 'c' }); + }); + it('should initialize props & context but not state in Component constructor', () => { // Not initializing state matches React behavior: https://codesandbox.io/s/rml19v8o2q class Foo extends Component {