diff --git a/packages/enzyme/src/ShallowWrapper.js b/packages/enzyme/src/ShallowWrapper.js index b0165110e..acf8080d7 100644 --- a/packages/enzyme/src/ShallowWrapper.js +++ b/packages/enzyme/src/ShallowWrapper.js @@ -243,6 +243,29 @@ function privateSetChildContext(adapter, wrapper, instance, renderedNode, getChi } } +function mockSCUIfgDSFPReturnNonNull(node, props, state) { + const { getDerivedStateFromProps } = node.type; + const { instance } = node; + if (typeof getDerivedStateFromProps === 'function') { + const gDSFPResult = getDerivedStateFromProps(props, state); + if (gDSFPResult != null) { + // we try to fix a React shallow renderer bug here. + // (facebook/react#14607, which has been fixed in react 16.8): + // when gDSFP return derived state, it will set instance state in shallow renderer before SCU, + // this will cause `this.state` in sCU be the updated state, which is wrong behavior. + // so we have to wrap sCU to pass the old state to original sCU. + const originalSCU = instance.shouldComponentUpdate; + instance.shouldComponentUpdate = (...args) => { + instance.state = state; + const sCUResult = originalSCU.apply(instance, args); + const nextState = args[1]; + instance.state = nextState; + instance.shouldComponentUpdate = originalSCU; + return sCUResult; + }; + } + } +} /** * @class ShallowWrapper @@ -452,6 +475,8 @@ class ShallowWrapper { && instance ) { if (typeof instance.shouldComponentUpdate === 'function') { + const nextProps = { ...prevProps, ...props }; + mockSCUIfgDSFPReturnNonNull(node, nextProps, state, instance); shouldComponentUpdateSpy = spyMethod(instance, 'shouldComponentUpdate'); } if ( @@ -601,6 +626,8 @@ class ShallowWrapper { && lifecycles.componentDidUpdate.onSetState && typeof instance.shouldComponentUpdate === 'function' ) { + const props = prevProps; + mockSCUIfgDSFPReturnNonNull(node, props, state, instance); shouldComponentUpdateSpy = spyMethod(instance, 'shouldComponentUpdate'); } if (