@@ -133,6 +133,7 @@ import {
133
133
isSuspenseInstanceFallback ,
134
134
registerSuspenseInstanceRetry ,
135
135
supportsHydration ,
136
+ isPrimaryRenderer ,
136
137
} from './ReactFiberHostConfig' ;
137
138
import type { SuspenseInstance } from './ReactFiberHostConfig' ;
138
139
import { shouldSuspend } from './ReactFiberReconciler' ;
@@ -151,6 +152,7 @@ import {findFirstSuspended} from './ReactFiberSuspenseComponent.new';
151
152
import {
152
153
pushProvider ,
153
154
propagateContextChange ,
155
+ propagateCacheRefresh ,
154
156
readContext ,
155
157
prepareToReadContext ,
156
158
calculateChangedBits ,
@@ -662,22 +664,82 @@ function updateCacheComponent(
662
664
return null ;
663
665
}
664
666
665
- const root = getWorkInProgressRoot ( ) ;
666
- invariant (
667
- root !== null ,
668
- 'Expected a work-in-progress root. This is a bug in React. Please ' +
669
- 'file an issue.' ,
670
- ) ;
667
+ // Read directly from the context. We don't set up a context dependency
668
+ // because the propagation function automatically includes CacheComponents in
669
+ // its search.
670
+ const parentCache : Cache | null = isPrimaryRenderer
671
+ ? CacheContext . _currentValue
672
+ : CacheContext . _currentValue2 ;
671
673
672
- const cache : Cache =
673
- current === null
674
- ? requestFreshCache ( root , renderLanes )
675
- : current . memoizedState ;
676
-
677
- // TODO: Propagate changes, once refreshing exists.
678
- pushProvider ( workInProgress , CacheContext , cache ) ;
674
+ let ownCache : Cache | null = null ;
675
+ if ( parentCache !== null && parentCache . providers === null ) {
676
+ // The parent boundary also has a new cache. We're either inside a new tree,
677
+ // or there was a refresh. In both cases, we should use the parent cache.
678
+ ownCache = null ;
679
+ } else {
680
+ if ( current === null ) {
681
+ // This is a newly mounted component. Request a fresh cache.
682
+ const root = getWorkInProgressRoot ( ) ;
683
+ invariant (
684
+ root !== null ,
685
+ 'Expected a work-in-progress root. This is a bug in React. Please ' +
686
+ 'file an issue.' ,
687
+ ) ;
688
+ const freshCache = requestFreshCache ( root , renderLanes ) ;
689
+ // This may be the same as the parent cache, like if the current render
690
+ // spawned from a previous render that already committed. Otherwise, this
691
+ // is the root of a cache consistency boundary.
692
+ if ( freshCache !== parentCache ) {
693
+ ownCache = freshCache ;
694
+ pushProvider ( workInProgress , CacheContext , freshCache ) ;
695
+ // No need to propagate the refresh, because this is a new tree.
696
+ } else {
697
+ // Use the parent cache
698
+ ownCache = null ;
699
+ }
700
+ } else {
701
+ // This component already mounted.
702
+ if ( includesSomeLane ( renderLanes , updateLanes ) ) {
703
+ // A refresh was scheduled.
704
+ const root = getWorkInProgressRoot ( ) ;
705
+ invariant (
706
+ root !== null ,
707
+ 'Expected a work-in-progress root. This is a bug in React. Please ' +
708
+ 'file an issue.' ,
709
+ ) ;
710
+ const freshCache = requestFreshCache ( root , renderLanes ) ;
711
+ if ( freshCache !== parentCache ) {
712
+ ownCache = freshCache ;
713
+ pushProvider ( workInProgress , CacheContext , freshCache ) ;
714
+ // Refreshes propagate through the entire subtree. The refreshed cache
715
+ // will override nested caches.
716
+ propagateCacheRefresh ( workInProgress , renderLanes ) ;
717
+ } else {
718
+ // The fresh cache is the same as the parent cache. I think this
719
+ // unreachable in practice, because this means the parent cache was
720
+ // refreshed in the same render. So we would have already handled this
721
+ // in the earlier branch, where we check if the parent is new.
722
+ ownCache = null ;
723
+ }
724
+ } else {
725
+ // Reuse the memoized cache.
726
+ const prevCache : Cache | null = current . memoizedState ;
727
+ if ( prevCache !== null ) {
728
+ ownCache = prevCache ;
729
+ // There was no refresh, so no need to propagate to nested boundaries.
730
+ pushProvider ( workInProgress , CacheContext , prevCache ) ;
731
+ } else {
732
+ ownCache = null ;
733
+ }
734
+ }
735
+ }
736
+ }
679
737
680
- workInProgress . memoizedState = cache ;
738
+ // If this CacheComponent is the root of its tree, then `memoizedState` will
739
+ // point to a cache object. Otherwise, a null state indicates that this
740
+ // CacheComponent inherits from a parent boundary. We can use this to infer
741
+ // whether to push/pop the cache context.
742
+ workInProgress . memoizedState = ownCache ;
681
743
682
744
const nextChildren = workInProgress . pendingProps . children ;
683
745
reconcileChildren ( current , workInProgress , nextChildren , renderLanes ) ;
@@ -3273,8 +3335,10 @@ function beginWork(
3273
3335
}
3274
3336
case CacheComponent: {
3275
3337
if ( enableCache ) {
3276
- const cache : Cache = current . memoizedState ;
3277
- pushProvider ( workInProgress , CacheContext , cache ) ;
3338
+ const ownCache : Cache | null = workInProgress . memoizedState ;
3339
+ if ( ownCache !== null ) {
3340
+ pushProvider ( workInProgress , CacheContext , ownCache ) ;
3341
+ }
3278
3342
}
3279
3343
break ;
3280
3344
}
0 commit comments