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 (
+