@@ -90,7 +90,7 @@ import {
9090 prepareToReadContext ,
9191 calculateChangedBits ,
9292} from './ReactFiberNewContext' ;
93- import { prepareToUseHooks , finishHooks , resetHooks } from './ReactFiberHooks' ;
93+ import { resetHooks , renderWithHooks , bailoutHooks } from './ReactFiberHooks' ;
9494import { stopProfilerTimerIfRunning } from './ReactProfilerTimer' ;
9595import {
9696 getMaskedContext ,
@@ -128,6 +128,8 @@ import {
128128
129129const ReactCurrentOwner = ReactSharedInternals . ReactCurrentOwner ;
130130
131+ let didReceiveUpdate : boolean = false ;
132+
131133let didWarnAboutBadClass ;
132134let didWarnAboutContextTypeOnFunctionComponent ;
133135let didWarnAboutGetDerivedStateOnFunctionComponent ;
@@ -237,16 +239,37 @@ function updateForwardRef(
237239 // The rest is a fork of updateFunctionComponent
238240 let nextChildren ;
239241 prepareToReadContext ( workInProgress , renderExpirationTime ) ;
240- prepareToUseHooks ( current , workInProgress , renderExpirationTime ) ;
241242 if ( __DEV__ ) {
242243 ReactCurrentOwner . current = workInProgress ;
243244 setCurrentPhase ( 'render' ) ;
244- nextChildren = render ( nextProps , ref ) ;
245+ nextChildren = renderWithHooks (
246+ current ,
247+ workInProgress ,
248+ render ,
249+ nextProps ,
250+ ref ,
251+ renderExpirationTime ,
252+ ) ;
245253 setCurrentPhase ( null ) ;
246254 } else {
247- nextChildren = render ( nextProps , ref ) ;
255+ nextChildren = renderWithHooks (
256+ current ,
257+ workInProgress ,
258+ render ,
259+ nextProps ,
260+ ref ,
261+ renderExpirationTime ,
262+ ) ;
263+ }
264+
265+ if ( current !== null && ! didReceiveUpdate ) {
266+ bailoutHooks ( current , workInProgress , renderExpirationTime ) ;
267+ return bailoutOnAlreadyFinishedWork (
268+ current ,
269+ workInProgress ,
270+ renderExpirationTime ,
271+ ) ;
248272 }
249- nextChildren = finishHooks ( render , nextProps , nextChildren , ref ) ;
250273
251274 // React DevTools reads this flag.
252275 workInProgress . effectTag |= PerformedWork ;
@@ -395,17 +418,20 @@ function updateSimpleMemoComponent(
395418 // Inner propTypes will be validated in the function component path.
396419 }
397420 }
398- if ( current !== null && updateExpirationTime < renderExpirationTime ) {
421+ if ( current !== null ) {
399422 const prevProps = current . memoizedProps ;
400423 if (
401424 shallowEqual ( prevProps , nextProps ) &&
402425 current . ref === workInProgress . ref
403426 ) {
404- return bailoutOnAlreadyFinishedWork (
405- current ,
406- workInProgress ,
407- renderExpirationTime ,
408- ) ;
427+ didReceiveUpdate = false ;
428+ if ( updateExpirationTime < renderExpirationTime ) {
429+ return bailoutOnAlreadyFinishedWork (
430+ current ,
431+ workInProgress ,
432+ renderExpirationTime ,
433+ ) ;
434+ }
409435 }
410436 }
411437 return updateFunctionComponent (
@@ -506,16 +532,37 @@ function updateFunctionComponent(
506532
507533 let nextChildren ;
508534 prepareToReadContext ( workInProgress , renderExpirationTime ) ;
509- prepareToUseHooks ( current , workInProgress , renderExpirationTime ) ;
510535 if ( __DEV__ ) {
511536 ReactCurrentOwner . current = workInProgress ;
512537 setCurrentPhase ( 'render' ) ;
513- nextChildren = Component ( nextProps , context ) ;
538+ nextChildren = renderWithHooks (
539+ current ,
540+ workInProgress ,
541+ Component ,
542+ nextProps ,
543+ context ,
544+ renderExpirationTime ,
545+ ) ;
514546 setCurrentPhase ( null ) ;
515547 } else {
516- nextChildren = Component ( nextProps , context ) ;
548+ nextChildren = renderWithHooks (
549+ current ,
550+ workInProgress ,
551+ Component ,
552+ nextProps ,
553+ context ,
554+ renderExpirationTime ,
555+ ) ;
556+ }
557+
558+ if ( current !== null && ! didReceiveUpdate ) {
559+ bailoutHooks ( current , workInProgress , renderExpirationTime ) ;
560+ return bailoutOnAlreadyFinishedWork (
561+ current ,
562+ workInProgress ,
563+ renderExpirationTime ,
564+ ) ;
517565 }
518- nextChildren = finishHooks ( Component , nextProps , nextChildren , context ) ;
519566
520567 // React DevTools reads this flag.
521568 workInProgress . effectTag |= PerformedWork ;
@@ -850,7 +897,7 @@ function updateHostComponent(current, workInProgress, renderExpirationTime) {
850897 shouldDeprioritizeSubtree ( type , nextProps )
851898 ) {
852899 // Schedule this fiber to re-render at offscreen priority. Then bailout.
853- workInProgress . expirationTime = Never ;
900+ workInProgress . expirationTime = workInProgress . childExpirationTime = Never ;
854901 return null ;
855902 }
856903
@@ -1063,7 +1110,6 @@ function mountIndeterminateComponent(
10631110 const context = getMaskedContext ( workInProgress , unmaskedContext ) ;
10641111
10651112 prepareToReadContext ( workInProgress , renderExpirationTime ) ;
1066- prepareToUseHooks ( null , workInProgress , renderExpirationTime ) ;
10671113
10681114 let value ;
10691115
@@ -1091,9 +1137,23 @@ function mountIndeterminateComponent(
10911137 }
10921138
10931139 ReactCurrentOwner . current = workInProgress ;
1094- value = Component ( props , context ) ;
1140+ value = renderWithHooks (
1141+ null ,
1142+ workInProgress ,
1143+ Component ,
1144+ props ,
1145+ context ,
1146+ renderExpirationTime ,
1147+ ) ;
10951148 } else {
1096- value = Component ( props , context ) ;
1149+ value = renderWithHooks (
1150+ null ,
1151+ workInProgress ,
1152+ Component ,
1153+ props ,
1154+ context ,
1155+ renderExpirationTime ,
1156+ ) ;
10971157 }
10981158 // React DevTools reads this flag.
10991159 workInProgress . effectTag |= PerformedWork ;
@@ -1147,7 +1207,6 @@ function mountIndeterminateComponent(
11471207 } else {
11481208 // Proceed under the assumption that this is a function component
11491209 workInProgress . tag = FunctionComponent ;
1150- value = finishHooks ( Component , props , value , context ) ;
11511210 reconcileChildren ( null , workInProgress , value , renderExpirationTime ) ;
11521211 if ( __DEV__ ) {
11531212 validateFunctionComponentInDev ( workInProgress , Component ) ;
@@ -1638,6 +1697,10 @@ function updateContextConsumer(
16381697 return workInProgress . child ;
16391698}
16401699
1700+ export function markWorkInProgressReceivedUpdate ( ) {
1701+ didReceiveUpdate = true ;
1702+ }
1703+
16411704function bailoutOnAlreadyFinishedWork (
16421705 current : Fiber | null ,
16431706 workInProgress : Fiber ,
@@ -1647,7 +1710,7 @@ function bailoutOnAlreadyFinishedWork(
16471710
16481711 if ( current !== null ) {
16491712 // Reuse previous context list
1650- workInProgress . firstContextDependency = current . firstContextDependency ;
1713+ workInProgress . contextDependencies = current . contextDependencies ;
16511714 }
16521715
16531716 if ( enableProfilerTimer ) {
@@ -1680,11 +1743,13 @@ function beginWork(
16801743 if ( current !== null ) {
16811744 const oldProps = current . memoizedProps ;
16821745 const newProps = workInProgress . pendingProps ;
1683- if (
1684- oldProps === newProps &&
1685- ! hasLegacyContextChanged ( ) &&
1686- updateExpirationTime < renderExpirationTime
1687- ) {
1746+
1747+ if ( oldProps !== newProps || hasLegacyContextChanged ( ) ) {
1748+ // If props or context changed, mark the fiber as having performed work.
1749+ // This may be unset if the props are determined to be equal later (memo).
1750+ didReceiveUpdate = true ;
1751+ } else if ( updateExpirationTime < renderExpirationTime ) {
1752+ didReceiveUpdate = false ;
16881753 // This fiber does not have any pending work. Bailout without entering
16891754 // the begin phase. There's still some bookkeeping we that needs to be done
16901755 // in this optimized path, mostly pushing stuff onto the stack.
@@ -1767,6 +1832,8 @@ function beginWork(
17671832 renderExpirationTime ,
17681833 ) ;
17691834 }
1835+ } else {
1836+ didReceiveUpdate = false ;
17701837 }
17711838
17721839 // Before entering the begin phase, clear the expiration time.
0 commit comments