@@ -29,7 +29,10 @@ import type {
29
29
import type { SuspenseContext } from './ReactFiberSuspenseContext.new' ;
30
30
import type { OffscreenState } from './ReactFiberOffscreenComponent' ;
31
31
import type { Cache , SpawnedCachePool } from './ReactFiberCacheComponent.new' ;
32
- import { enableSuspenseAvoidThisFallback } from 'shared/ReactFeatureFlags' ;
32
+ import {
33
+ enableClientRenderFallbackOnHydrationMismatch ,
34
+ enableSuspenseAvoidThisFallback ,
35
+ } from 'shared/ReactFeatureFlags' ;
33
36
34
37
import { resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions } from './ReactMutableSource.new' ;
35
38
@@ -74,6 +77,9 @@ import {
74
77
StaticMask ,
75
78
MutationMask ,
76
79
Passive ,
80
+ Incomplete ,
81
+ ShouldCapture ,
82
+ ForceClientRender ,
77
83
} from './ReactFiberFlags' ;
78
84
79
85
import {
@@ -120,9 +126,11 @@ import {
120
126
prepareToHydrateHostInstance ,
121
127
prepareToHydrateHostTextInstance ,
122
128
prepareToHydrateHostSuspenseInstance ,
129
+ warnDeleteNextHydratableInstance ,
123
130
popHydrationState ,
124
131
resetHydrationState ,
125
132
getIsHydrating ,
133
+ hasMore ,
126
134
} from './ReactFiberHydrationContext.new' ;
127
135
import {
128
136
enableSuspenseCallback ,
@@ -828,7 +836,6 @@ function completeWork(
828
836
// to the current tree provider fiber is just as fast and less error-prone.
829
837
// Ideally we would have a special version of the work loop only
830
838
// for hydration.
831
- popTreeContext ( workInProgress ) ;
832
839
switch ( workInProgress . tag ) {
833
840
case IndeterminateComponent :
834
841
case LazyComponent :
@@ -840,9 +847,11 @@ function completeWork(
840
847
case Profiler :
841
848
case ContextConsumer :
842
849
case MemoComponent :
850
+ popTreeContext ( workInProgress ) ;
843
851
bubbleProperties ( workInProgress ) ;
844
852
return null ;
845
853
case ClassComponent : {
854
+ popTreeContext ( workInProgress ) ;
846
855
const Component = workInProgress . type ;
847
856
if ( isLegacyContextProvider ( Component ) ) {
848
857
popLegacyContext ( workInProgress ) ;
@@ -852,6 +861,23 @@ function completeWork(
852
861
}
853
862
case HostRoot : {
854
863
const fiberRoot = ( workInProgress . stateNode : FiberRoot ) ;
864
+ if ( current === null || current . child === null ) {
865
+ // If we hydrated, pop so that we can delete any remaining children
866
+ // that weren't hydrated.
867
+ const wasHydrated = popHydrationState ( workInProgress ) ;
868
+ if ( wasHydrated ) {
869
+ // If we hydrated, then we'll need to schedule an update for
870
+ // the commit side-effects on the root.
871
+ markUpdate ( workInProgress ) ;
872
+ } else if ( ! fiberRoot . isDehydrated ) {
873
+ // Schedule an effect to clear this container at the start of the next commit.
874
+ // This handles the case of React rendering into a container with previous children.
875
+ // It's also safe to do for updates too, because current.child would only be null
876
+ // if the previous render was null (so the container would already be empty).
877
+ workInProgress . flags |= Snapshot ;
878
+ }
879
+ }
880
+ popTreeContext ( workInProgress ) ;
855
881
if ( enableCache ) {
856
882
popRootCachePool ( fiberRoot , renderLanes ) ;
857
883
@@ -873,27 +899,13 @@ function completeWork(
873
899
fiberRoot . context = fiberRoot . pendingContext ;
874
900
fiberRoot . pendingContext = null ;
875
901
}
876
- if ( current === null || current . child === null ) {
877
- // If we hydrated, pop so that we can delete any remaining children
878
- // that weren't hydrated.
879
- const wasHydrated = popHydrationState ( workInProgress ) ;
880
- if ( wasHydrated ) {
881
- // If we hydrated, then we'll need to schedule an update for
882
- // the commit side-effects on the root.
883
- markUpdate ( workInProgress ) ;
884
- } else if ( ! fiberRoot . isDehydrated ) {
885
- // Schedule an effect to clear this container at the start of the next commit.
886
- // This handles the case of React rendering into a container with previous children.
887
- // It's also safe to do for updates too, because current.child would only be null
888
- // if the previous render was null (so the container would already be empty).
889
- workInProgress . flags |= Snapshot ;
890
- }
891
- }
892
902
updateHostContainer ( current , workInProgress ) ;
893
903
bubbleProperties ( workInProgress ) ;
894
904
return null ;
895
905
}
896
906
case HostComponent : {
907
+ const wasHydrated = popHydrationState ( workInProgress ) ;
908
+ popTreeContext ( workInProgress ) ;
897
909
popHostContext ( workInProgress ) ;
898
910
const rootContainerInstance = getRootHostContainer ( ) ;
899
911
const type = workInProgress . type ;
@@ -928,7 +940,6 @@ function completeWork(
928
940
// "stack" as the parent. Then append children as we go in beginWork
929
941
// or completeWork depending on whether we want to add them top->down or
930
942
// bottom->up. Top->down is faster in IE11.
931
- const wasHydrated = popHydrationState ( workInProgress ) ;
932
943
if ( wasHydrated ) {
933
944
// TODO: Move this and createInstance step into the beginPhase
934
945
// to consolidate.
@@ -981,6 +992,7 @@ function completeWork(
981
992
return null ;
982
993
}
983
994
case HostText : {
995
+ popTreeContext ( workInProgress ) ;
984
996
const newText = newProps ;
985
997
if ( current && workInProgress . stateNode != null ) {
986
998
const oldText = current . memoizedProps ;
@@ -1017,14 +1029,27 @@ function completeWork(
1017
1029
return null ;
1018
1030
}
1019
1031
case SuspenseComponent : {
1020
- popSuspenseContext ( workInProgress ) ;
1021
1032
const nextState : null | SuspenseState = workInProgress . memoizedState ;
1022
-
1023
1033
if ( enableSuspenseServerRenderer ) {
1034
+ if (
1035
+ enableClientRenderFallbackOnHydrationMismatch &&
1036
+ hasMore ( ) &&
1037
+ ( workInProgress . flags & DidCapture ) === NoFlags
1038
+ ) {
1039
+ warnDeleteNextHydratableInstance ( workInProgress ) ;
1040
+ resetHydrationState ( ) ;
1041
+ workInProgress . flags |=
1042
+ ForceClientRender | Incomplete | ShouldCapture ;
1043
+ popTreeContext ( workInProgress ) ;
1044
+ popSuspenseContext ( workInProgress ) ;
1045
+ return workInProgress ;
1046
+ }
1024
1047
if ( nextState !== null && nextState . dehydrated !== null ) {
1025
1048
// We might be inside a hydration state the first time we're picking up this
1026
1049
// Suspense boundary, and also after we've reentered it for further hydration.
1027
1050
const wasHydrated = popHydrationState ( workInProgress ) ;
1051
+ popTreeContext ( workInProgress ) ;
1052
+ popSuspenseContext ( workInProgress ) ;
1028
1053
if ( current === null ) {
1029
1054
if ( ! wasHydrated ) {
1030
1055
throw new Error (
@@ -1091,6 +1116,8 @@ function completeWork(
1091
1116
) {
1092
1117
transferActualDuration ( workInProgress ) ;
1093
1118
}
1119
+ popTreeContext ( workInProgress ) ;
1120
+ popSuspenseContext ( workInProgress ) ;
1094
1121
// Don't bubble properties in this case.
1095
1122
return workInProgress ;
1096
1123
}
@@ -1103,6 +1130,8 @@ function completeWork(
1103
1130
const prevState : null | SuspenseState = current . memoizedState ;
1104
1131
prevDidTimeout = prevState !== null ;
1105
1132
}
1133
+ popTreeContext ( workInProgress ) ;
1134
+ popSuspenseContext ( workInProgress ) ;
1106
1135
1107
1136
if ( enableCache && nextDidTimeout ) {
1108
1137
const offscreenFiber : Fiber = ( workInProgress . child : any ) ;
@@ -1207,6 +1236,7 @@ function completeWork(
1207
1236
return null ;
1208
1237
}
1209
1238
case HostPortal :
1239
+ popTreeContext ( workInProgress ) ;
1210
1240
popHostContainer ( workInProgress ) ;
1211
1241
updateHostContainer ( current , workInProgress ) ;
1212
1242
if ( current === null ) {
@@ -1215,12 +1245,14 @@ function completeWork(
1215
1245
bubbleProperties ( workInProgress ) ;
1216
1246
return null ;
1217
1247
case ContextProvider:
1248
+ popTreeContext ( workInProgress ) ;
1218
1249
// Pop provider fiber
1219
1250
const context : ReactContext < any > = workInProgress.type._context;
1220
1251
popProvider(context, workInProgress);
1221
1252
bubbleProperties(workInProgress);
1222
1253
return null;
1223
1254
case IncompleteClassComponent: {
1255
+ popTreeContext ( workInProgress ) ;
1224
1256
// Same as class component case. I put it down here so that the tags are
1225
1257
// sequential to ensure this switch is compiled to a jump table.
1226
1258
const Component = workInProgress . type ;
@@ -1231,6 +1263,7 @@ function completeWork(
1231
1263
return null ;
1232
1264
}
1233
1265
case SuspenseListComponent: {
1266
+ popTreeContext ( workInProgress ) ;
1234
1267
popSuspenseContext ( workInProgress ) ;
1235
1268
1236
1269
const renderState : null | SuspenseListRenderState =
@@ -1440,6 +1473,7 @@ function completeWork(
1440
1473
return null ;
1441
1474
}
1442
1475
case ScopeComponent: {
1476
+ popTreeContext ( workInProgress ) ;
1443
1477
if ( enableScopeAPI ) {
1444
1478
if ( current === null ) {
1445
1479
const scopeInstance : ReactScopeInstance = createScopeInstance ( ) ;
@@ -1464,6 +1498,7 @@ function completeWork(
1464
1498
}
1465
1499
case OffscreenComponent:
1466
1500
case LegacyHiddenComponent: {
1501
+ popTreeContext ( workInProgress ) ;
1467
1502
popRenderLanes ( workInProgress ) ;
1468
1503
const nextState : OffscreenState | null = workInProgress . memoizedState ;
1469
1504
const nextIsHidden = nextState !== null ;
@@ -1532,6 +1567,7 @@ function completeWork(
1532
1567
return null ;
1533
1568
}
1534
1569
case CacheComponent: {
1570
+ popTreeContext ( workInProgress ) ;
1535
1571
if ( enableCache ) {
1536
1572
let previousCache : Cache | null = null ;
1537
1573
if ( workInProgress . alternate !== null ) {
0 commit comments