@@ -131,6 +131,10 @@ function getAdapterLifecycles({ options }) {
131131 } ) ,
132132 }
133133 : null ;
134+ const { getDerivedStateFromProps : originalGDSFP } = lifecycles ;
135+ const getDerivedStateFromProps = originalGDSFP ? {
136+ hasShouldComponentUpdateBug : ! ! originalGDSFP . hasShouldComponentUpdateBug ,
137+ } : false ;
134138
135139 return {
136140 ...lifecycles ,
@@ -142,6 +146,7 @@ function getAdapterLifecycles({ options }) {
142146 ...lifecycles . getChildContext ,
143147 } ,
144148 ...( componentDidUpdate && { componentDidUpdate } ) ,
149+ getDerivedStateFromProps,
145150 } ;
146151}
147152
@@ -243,6 +248,28 @@ function privateSetChildContext(adapter, wrapper, instance, renderedNode, getChi
243248 }
244249}
245250
251+ function mockSCUIfgDSFPReturnNonNull ( node , state ) {
252+ const { getDerivedStateFromProps } = node . type ;
253+
254+ if ( typeof getDerivedStateFromProps === 'function' ) {
255+ // we try to fix a React shallow renderer bug here.
256+ // (facebook/react#14607, which has been fixed in react 16.8):
257+ // when gDSFP return derived state, it will set instance state in shallow renderer before SCU,
258+ // this will cause `this.state` in sCU be the updated state, which is wrong behavior.
259+ // so we have to wrap sCU to pass the old state to original sCU.
260+ const { instance } = node ;
261+ const { restore } = spyMethod ( instance , 'shouldComponentUpdate' , ( originalSCU ) => {
262+ return function shouldComponentUpdate ( ...args ) {
263+ instance . state = state ;
264+ const sCUResult = originalSCU . apply ( instance , args ) ;
265+ const [ , nextState ] = args ;
266+ instance . state = nextState ;
267+ restore ( ) ;
268+ return sCUResult ;
269+ } ;
270+ } ) ;
271+ }
272+ }
246273
247274/**
248275 * @class ShallowWrapper
@@ -452,6 +479,10 @@ class ShallowWrapper {
452479 && instance
453480 ) {
454481 if ( typeof instance . shouldComponentUpdate === 'function' ) {
482+ const { getDerivedStateFromProps : gDSFP } = lifecycles ;
483+ if ( gDSFP && gDSFP . hasShouldComponentUpdateBug ) {
484+ mockSCUIfgDSFPReturnNonNull ( node , state ) ;
485+ }
455486 shouldComponentUpdateSpy = spyMethod ( instance , 'shouldComponentUpdate' ) ;
456487 }
457488 if (
@@ -601,6 +632,10 @@ class ShallowWrapper {
601632 && lifecycles . componentDidUpdate . onSetState
602633 && typeof instance . shouldComponentUpdate === 'function'
603634 ) {
635+ const { getDerivedStateFromProps : gDSFP } = lifecycles ;
636+ if ( gDSFP && gDSFP . hasShouldComponentUpdateBug ) {
637+ mockSCUIfgDSFPReturnNonNull ( node , state ) ;
638+ }
604639 shouldComponentUpdateSpy = spyMethod ( instance , 'shouldComponentUpdate' ) ;
605640 }
606641 if (
0 commit comments