From da8180f64b254a1f79811877bb0187529a6cd0e3 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Sat, 4 Feb 2023 09:09:11 +0100 Subject: [PATCH] avoid bailing in strict equality --- src/diff/index.js | 12 +++-- .../lifecycles/shouldComponentUpdate.test.js | 47 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/diff/index.js b/src/diff/index.js index 242bf84581..eae5afb9cf 100644 --- a/src/diff/index.js +++ b/src/diff/index.js @@ -143,10 +143,16 @@ export function diff( ) === false) || newVNode._original === oldVNode._original ) { - c.props = newProps; - c.state = c._nextState; // More info about this here: https://gist.github.com/JoviDeCroock/bec5f2ce93544d2e6070ef8e0036e4e8 - if (newVNode._original !== oldVNode._original) c._dirty = false; + if (newVNode._original !== oldVNode._original) { + // When we are dealing with a bail because of sCU we have to update + // the props, state and dirty-state. + // when we are dealing with strict-equality we don't as the child could still + // be dirtied see #3883 + c.props = newProps; + c.state = c._nextState; + c._dirty = false; + } newVNode._dom = oldVNode._dom; newVNode._children = oldVNode._children; newVNode._children.forEach(vnode => { diff --git a/test/browser/lifecycles/shouldComponentUpdate.test.js b/test/browser/lifecycles/shouldComponentUpdate.test.js index 6b5f18709a..aa7ff440d5 100644 --- a/test/browser/lifecycles/shouldComponentUpdate.test.js +++ b/test/browser/lifecycles/shouldComponentUpdate.test.js @@ -858,6 +858,53 @@ describe('Lifecycle methods', () => { // ]); }); + it('should correctly handle double state updates', () => { + let updateParent, updateChild; + class Parent extends Component { + state = { text: 'parent-old' }; + + componentDidMount() { + updateParent = () => this.setState({ text: 'Parent-NEW' }); + } + + render() { + return ( + + {this.props.children} and {this.state.text} + + ); + } + } + + class Child extends Component { + state = { text: 'child-old' }; + + shouldComponentUpdate(nextProps, nextState) { + return this.state.text !== nextState.text; + } + + componentDidMount() { + updateChild = () => this.setState({ text: 'Child-NEW' }); + } + + render() { + return

{this.state.text}

; + } + } + + render( + + + , + scratch + ); + + updateParent(); + updateChild(); + rerender(); + expect(scratch.innerHTML).to.equal('

Child-NEW

and Parent-NEW'); + }); + it('should maintain the order if memoised component initially rendered empty content', () => { let showText, updateParent; class Child extends Component {