@@ -90,7 +90,7 @@ import {
90
90
prepareToReadContext ,
91
91
calculateChangedBits ,
92
92
} from './ReactFiberNewContext' ;
93
- import { prepareToUseHooks , finishHooks , resetHooks } from './ReactFiberHooks' ;
93
+ import { resetHooks , renderWithHooks , bailoutHooks } from './ReactFiberHooks' ;
94
94
import { stopProfilerTimerIfRunning } from './ReactProfilerTimer' ;
95
95
import {
96
96
getMaskedContext ,
@@ -128,6 +128,8 @@ import {
128
128
129
129
const ReactCurrentOwner = ReactSharedInternals . ReactCurrentOwner ;
130
130
131
+ let didReceiveUpdate : boolean = false ;
132
+
131
133
let didWarnAboutBadClass ;
132
134
let didWarnAboutContextTypeOnFunctionComponent ;
133
135
let didWarnAboutGetDerivedStateOnFunctionComponent ;
@@ -237,16 +239,37 @@ function updateForwardRef(
237
239
// The rest is a fork of updateFunctionComponent
238
240
let nextChildren ;
239
241
prepareToReadContext ( workInProgress , renderExpirationTime ) ;
240
- prepareToUseHooks ( current , workInProgress , renderExpirationTime ) ;
241
242
if ( __DEV__ ) {
242
243
ReactCurrentOwner . current = workInProgress ;
243
244
setCurrentPhase ( 'render' ) ;
244
- nextChildren = render ( nextProps , ref ) ;
245
+ nextChildren = renderWithHooks (
246
+ current ,
247
+ workInProgress ,
248
+ render ,
249
+ nextProps ,
250
+ ref ,
251
+ renderExpirationTime ,
252
+ ) ;
245
253
setCurrentPhase ( null ) ;
246
254
} 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
+ ) ;
248
272
}
249
- nextChildren = finishHooks ( render , nextProps , nextChildren , ref ) ;
250
273
251
274
// React DevTools reads this flag.
252
275
workInProgress . effectTag |= PerformedWork ;
@@ -395,17 +418,20 @@ function updateSimpleMemoComponent(
395
418
// Inner propTypes will be validated in the function component path.
396
419
}
397
420
}
398
- if ( current !== null && updateExpirationTime < renderExpirationTime ) {
421
+ if ( current !== null ) {
399
422
const prevProps = current . memoizedProps ;
400
423
if (
401
424
shallowEqual ( prevProps , nextProps ) &&
402
425
current . ref === workInProgress . ref
403
426
) {
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
+ }
409
435
}
410
436
}
411
437
return updateFunctionComponent (
@@ -506,16 +532,37 @@ function updateFunctionComponent(
506
532
507
533
let nextChildren ;
508
534
prepareToReadContext ( workInProgress , renderExpirationTime ) ;
509
- prepareToUseHooks ( current , workInProgress , renderExpirationTime ) ;
510
535
if ( __DEV__ ) {
511
536
ReactCurrentOwner . current = workInProgress ;
512
537
setCurrentPhase ( 'render' ) ;
513
- nextChildren = Component ( nextProps , context ) ;
538
+ nextChildren = renderWithHooks (
539
+ current ,
540
+ workInProgress ,
541
+ Component ,
542
+ nextProps ,
543
+ context ,
544
+ renderExpirationTime ,
545
+ ) ;
514
546
setCurrentPhase ( null ) ;
515
547
} 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
+ ) ;
517
565
}
518
- nextChildren = finishHooks ( Component , nextProps , nextChildren , context ) ;
519
566
520
567
// React DevTools reads this flag.
521
568
workInProgress . effectTag |= PerformedWork ;
@@ -850,7 +897,7 @@ function updateHostComponent(current, workInProgress, renderExpirationTime) {
850
897
shouldDeprioritizeSubtree ( type , nextProps )
851
898
) {
852
899
// Schedule this fiber to re-render at offscreen priority. Then bailout.
853
- workInProgress . expirationTime = Never ;
900
+ workInProgress . expirationTime = workInProgress . childExpirationTime = Never ;
854
901
return null ;
855
902
}
856
903
@@ -1063,7 +1110,6 @@ function mountIndeterminateComponent(
1063
1110
const context = getMaskedContext ( workInProgress , unmaskedContext ) ;
1064
1111
1065
1112
prepareToReadContext ( workInProgress , renderExpirationTime ) ;
1066
- prepareToUseHooks ( null , workInProgress , renderExpirationTime ) ;
1067
1113
1068
1114
let value ;
1069
1115
@@ -1091,9 +1137,23 @@ function mountIndeterminateComponent(
1091
1137
}
1092
1138
1093
1139
ReactCurrentOwner . current = workInProgress ;
1094
- value = Component ( props , context ) ;
1140
+ value = renderWithHooks (
1141
+ null ,
1142
+ workInProgress ,
1143
+ Component ,
1144
+ props ,
1145
+ context ,
1146
+ renderExpirationTime ,
1147
+ ) ;
1095
1148
} else {
1096
- value = Component ( props , context ) ;
1149
+ value = renderWithHooks (
1150
+ null ,
1151
+ workInProgress ,
1152
+ Component ,
1153
+ props ,
1154
+ context ,
1155
+ renderExpirationTime ,
1156
+ ) ;
1097
1157
}
1098
1158
// React DevTools reads this flag.
1099
1159
workInProgress . effectTag |= PerformedWork ;
@@ -1147,7 +1207,6 @@ function mountIndeterminateComponent(
1147
1207
} else {
1148
1208
// Proceed under the assumption that this is a function component
1149
1209
workInProgress . tag = FunctionComponent ;
1150
- value = finishHooks ( Component , props , value , context ) ;
1151
1210
reconcileChildren ( null , workInProgress , value , renderExpirationTime ) ;
1152
1211
if ( __DEV__ ) {
1153
1212
validateFunctionComponentInDev ( workInProgress , Component ) ;
@@ -1638,6 +1697,10 @@ function updateContextConsumer(
1638
1697
return workInProgress . child ;
1639
1698
}
1640
1699
1700
+ export function markWorkInProgressReceivedUpdate ( ) {
1701
+ didReceiveUpdate = true ;
1702
+ }
1703
+
1641
1704
function bailoutOnAlreadyFinishedWork (
1642
1705
current : Fiber | null ,
1643
1706
workInProgress : Fiber ,
@@ -1647,7 +1710,7 @@ function bailoutOnAlreadyFinishedWork(
1647
1710
1648
1711
if ( current !== null ) {
1649
1712
// Reuse previous context list
1650
- workInProgress . firstContextDependency = current . firstContextDependency ;
1713
+ workInProgress . contextDependencies = current . contextDependencies ;
1651
1714
}
1652
1715
1653
1716
if ( enableProfilerTimer ) {
@@ -1680,11 +1743,13 @@ function beginWork(
1680
1743
if ( current !== null ) {
1681
1744
const oldProps = current . memoizedProps ;
1682
1745
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 ;
1688
1753
// This fiber does not have any pending work. Bailout without entering
1689
1754
// the begin phase. There's still some bookkeeping we that needs to be done
1690
1755
// in this optimized path, mostly pushing stuff onto the stack.
@@ -1767,6 +1832,8 @@ function beginWork(
1767
1832
renderExpirationTime ,
1768
1833
) ;
1769
1834
}
1835
+ } else {
1836
+ didReceiveUpdate = false ;
1770
1837
}
1771
1838
1772
1839
// Before entering the begin phase, clear the expiration time.
0 commit comments