Skip to content

Commit b13ea9e

Browse files
committed
Refreshes should not affect "sibling" boundaries
I had thought we decided that refreshing a boundary would also refresh all the content that is currently consistent (i.e. shared the same underlying cache) with it, but I was wrong. Refreshing should only affect the nearest tree and its descendents. "Sibling" content will intentionally be inconsistent after the refresh. This allows me to drop the subscription stuff, which is nice.
1 parent 92685bd commit b13ea9e

14 files changed

+178
-262
lines changed

packages/react-reconciler/src/ReactFiberBeginWork.new.js

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type {
2323
OffscreenProps,
2424
OffscreenState,
2525
} from './ReactFiberOffscreenComponent';
26-
import type {Cache} from './ReactFiberCacheComponent';
26+
import type {CacheInstance} from './ReactFiberCacheComponent';
2727

2828
import checkPropTypes from 'shared/checkPropTypes';
2929

@@ -667,17 +667,17 @@ function updateCacheComponent(
667667
// Read directly from the context. We don't set up a context dependency
668668
// because the propagation function automatically includes CacheComponents in
669669
// its search.
670-
const parentCache: Cache | null = isPrimaryRenderer
670+
const parentCache: CacheInstance | null = isPrimaryRenderer
671671
? CacheContext._currentValue
672672
: CacheContext._currentValue2;
673673

674-
let ownCache: Cache | null = null;
674+
let ownCacheInstance: CacheInstance | null = null;
675675
// TODO: Fast path if parent has a new cache. Merely an optimization. Might
676676
// not be worth it.
677677
if (false) {
678678
// The parent boundary also has a new cache. We're either inside a new tree,
679679
// or there was a refresh. In both cases, we should use the parent cache.
680-
ownCache = null;
680+
ownCacheInstance = null;
681681
} else {
682682
if (current === null) {
683683
// This is a newly mounted component. Request a fresh cache.
@@ -692,12 +692,15 @@ function updateCacheComponent(
692692
// spawned from a previous render that already committed. Otherwise, this
693693
// is the root of a cache consistency boundary.
694694
if (freshCache !== parentCache) {
695-
ownCache = freshCache;
696-
pushProvider(workInProgress, CacheContext, freshCache);
695+
ownCacheInstance = {
696+
cache: freshCache,
697+
provider: workInProgress,
698+
};
699+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
697700
// No need to propagate the refresh, because this is a new tree.
698701
} else {
699702
// Use the parent cache
700-
ownCache = null;
703+
ownCacheInstance = null;
701704
}
702705
} else {
703706
// This component already mounted.
@@ -711,8 +714,11 @@ function updateCacheComponent(
711714
);
712715
const freshCache = requestFreshCache(root, renderLanes);
713716
if (freshCache !== parentCache) {
714-
ownCache = freshCache;
715-
pushProvider(workInProgress, CacheContext, freshCache);
717+
ownCacheInstance = {
718+
cache: freshCache,
719+
provider: workInProgress,
720+
};
721+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
716722
// Refreshes propagate through the entire subtree. The refreshed cache
717723
// will override nested caches.
718724
propagateCacheRefresh(workInProgress, renderLanes);
@@ -721,17 +727,17 @@ function updateCacheComponent(
721727
// unreachable in practice, because this means the parent cache was
722728
// refreshed in the same render. So we would have already handled this
723729
// in the earlier branch, where we check if the parent is new.
724-
ownCache = null;
730+
ownCacheInstance = null;
725731
}
726732
} else {
727733
// Reuse the memoized cache.
728-
const prevCache: Cache | null = current.memoizedState;
729-
if (prevCache !== null) {
730-
ownCache = prevCache;
734+
const prevCacheInstance: CacheInstance | null = current.memoizedState;
735+
if (prevCacheInstance !== null) {
736+
ownCacheInstance = prevCacheInstance;
731737
// There was no refresh, so no need to propagate to nested boundaries.
732-
pushProvider(workInProgress, CacheContext, prevCache);
738+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
733739
} else {
734-
ownCache = null;
740+
ownCacheInstance = null;
735741
}
736742
}
737743
}
@@ -741,7 +747,7 @@ function updateCacheComponent(
741747
// point to a cache object. Otherwise, a null state indicates that this
742748
// CacheComponent inherits from a parent boundary. We can use this to infer
743749
// whether to push/pop the cache context.
744-
workInProgress.memoizedState = ownCache;
750+
workInProgress.memoizedState = ownCacheInstance;
745751

746752
const nextChildren = workInProgress.pendingProps.children;
747753
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
@@ -3337,9 +3343,10 @@ function beginWork(
33373343
}
33383344
case CacheComponent: {
33393345
if (enableCache) {
3340-
const ownCache: Cache | null = workInProgress.memoizedState;
3341-
if (ownCache !== null) {
3342-
pushProvider(workInProgress, CacheContext, ownCache);
3346+
const ownCacheInstance: CacheInstance | null =
3347+
workInProgress.memoizedState;
3348+
if (ownCacheInstance !== null) {
3349+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
33433350
}
33443351
}
33453352
break;

packages/react-reconciler/src/ReactFiberBeginWork.old.js

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type {
2323
OffscreenProps,
2424
OffscreenState,
2525
} from './ReactFiberOffscreenComponent';
26-
import type {Cache} from './ReactFiberCacheComponent';
26+
import type {CacheInstance} from './ReactFiberCacheComponent';
2727

2828
import checkPropTypes from 'shared/checkPropTypes';
2929

@@ -667,17 +667,17 @@ function updateCacheComponent(
667667
// Read directly from the context. We don't set up a context dependency
668668
// because the propagation function automatically includes CacheComponents in
669669
// its search.
670-
const parentCache: Cache | null = isPrimaryRenderer
670+
const parentCache: CacheInstance | null = isPrimaryRenderer
671671
? CacheContext._currentValue
672672
: CacheContext._currentValue2;
673673

674-
let ownCache: Cache | null = null;
674+
let ownCacheInstance: CacheInstance | null = null;
675675
// TODO: Fast path if parent has a new cache. Merely an optimization. Might
676676
// not be worth it.
677677
if (false) {
678678
// The parent boundary also has a new cache. We're either inside a new tree,
679679
// or there was a refresh. In both cases, we should use the parent cache.
680-
ownCache = null;
680+
ownCacheInstance = null;
681681
} else {
682682
if (current === null) {
683683
// This is a newly mounted component. Request a fresh cache.
@@ -692,12 +692,15 @@ function updateCacheComponent(
692692
// spawned from a previous render that already committed. Otherwise, this
693693
// is the root of a cache consistency boundary.
694694
if (freshCache !== parentCache) {
695-
ownCache = freshCache;
696-
pushProvider(workInProgress, CacheContext, freshCache);
695+
ownCacheInstance = {
696+
cache: freshCache,
697+
provider: workInProgress,
698+
};
699+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
697700
// No need to propagate the refresh, because this is a new tree.
698701
} else {
699702
// Use the parent cache
700-
ownCache = null;
703+
ownCacheInstance = null;
701704
}
702705
} else {
703706
// This component already mounted.
@@ -711,8 +714,11 @@ function updateCacheComponent(
711714
);
712715
const freshCache = requestFreshCache(root, renderLanes);
713716
if (freshCache !== parentCache) {
714-
ownCache = freshCache;
715-
pushProvider(workInProgress, CacheContext, freshCache);
717+
ownCacheInstance = {
718+
cache: freshCache,
719+
provider: workInProgress,
720+
};
721+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
716722
// Refreshes propagate through the entire subtree. The refreshed cache
717723
// will override nested caches.
718724
propagateCacheRefresh(workInProgress, renderLanes);
@@ -721,17 +727,17 @@ function updateCacheComponent(
721727
// unreachable in practice, because this means the parent cache was
722728
// refreshed in the same render. So we would have already handled this
723729
// in the earlier branch, where we check if the parent is new.
724-
ownCache = null;
730+
ownCacheInstance = null;
725731
}
726732
} else {
727733
// Reuse the memoized cache.
728-
const prevCache: Cache | null = current.memoizedState;
729-
if (prevCache !== null) {
730-
ownCache = prevCache;
734+
const prevCacheInstance: CacheInstance | null = current.memoizedState;
735+
if (prevCacheInstance !== null) {
736+
ownCacheInstance = prevCacheInstance;
731737
// There was no refresh, so no need to propagate to nested boundaries.
732-
pushProvider(workInProgress, CacheContext, prevCache);
738+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
733739
} else {
734-
ownCache = null;
740+
ownCacheInstance = null;
735741
}
736742
}
737743
}
@@ -741,7 +747,7 @@ function updateCacheComponent(
741747
// point to a cache object. Otherwise, a null state indicates that this
742748
// CacheComponent inherits from a parent boundary. We can use this to infer
743749
// whether to push/pop the cache context.
744-
workInProgress.memoizedState = ownCache;
750+
workInProgress.memoizedState = ownCacheInstance;
745751

746752
const nextChildren = workInProgress.pendingProps.children;
747753
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
@@ -3337,9 +3343,10 @@ function beginWork(
33373343
}
33383344
case CacheComponent: {
33393345
if (enableCache) {
3340-
const ownCache: Cache | null = workInProgress.memoizedState;
3341-
if (ownCache !== null) {
3342-
pushProvider(workInProgress, CacheContext, ownCache);
3346+
const ownCacheInstance: CacheInstance | null =
3347+
workInProgress.memoizedState;
3348+
if (ownCacheInstance !== null) {
3349+
pushProvider(workInProgress, CacheContext, ownCacheInstance);
33433350
}
33443351
}
33453352
break;

packages/react-reconciler/src/ReactFiberCacheComponent.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import type {ReactContext} from 'shared/ReactTypes';
1111

1212
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
1313

14-
export type Cache = {|
15-
providers: Set<Fiber> | null,
16-
data: Map<() => mixed, mixed> | null,
14+
export type Cache = Map<() => mixed, mixed>;
15+
16+
export type CacheInstance = {|
17+
cache: Cache | null,
18+
provider: Fiber,
1719
|};
1820

19-
export const CacheContext: ReactContext<Cache | null> = {
21+
export const CacheContext: ReactContext<CacheInstance | null> = {
2022
$$typeof: REACT_CONTEXT_TYPE,
2123
// We don't use Consumer/Provider for Cache components. So we'll cheat.
2224
Consumer: (null: any),

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import {
3636
enableFundamentalAPI,
3737
enableSuspenseCallback,
3838
enableScopeAPI,
39-
enableCache,
4039
} from 'shared/ReactFeatureFlags';
4140
import {
4241
FunctionComponent,
@@ -793,31 +792,8 @@ function commitLifeCycles(
793792
case ScopeComponent:
794793
case OffscreenComponent:
795794
case LegacyHiddenComponent:
795+
case CacheComponent:
796796
return;
797-
case CacheComponent: {
798-
if (enableCache) {
799-
if (current !== null) {
800-
const oldCache: Cache | null = current.memoizedState;
801-
if (oldCache !== null) {
802-
const oldCacheProviders = oldCache.providers;
803-
if (oldCacheProviders) {
804-
oldCacheProviders.delete(current);
805-
oldCacheProviders.delete(finishedWork);
806-
}
807-
}
808-
}
809-
const newCache: Cache | null = finishedWork.memoizedState;
810-
if (newCache !== null) {
811-
const newCacheProviders = newCache.providers;
812-
if (newCacheProviders === null) {
813-
newCache.providers = new Set([finishedWork]);
814-
} else {
815-
newCacheProviders.add(finishedWork);
816-
}
817-
}
818-
}
819-
return;
820-
}
821797
}
822798
invariant(
823799
false,

packages/react-reconciler/src/ReactFiberCommitWork.old.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import {
3737
enableSuspenseCallback,
3838
enableScopeAPI,
3939
enableDoubleInvokingEffects,
40-
enableCache,
4140
} from 'shared/ReactFeatureFlags';
4241
import {
4342
FunctionComponent,
@@ -794,31 +793,8 @@ function commitLifeCycles(
794793
case ScopeComponent:
795794
case OffscreenComponent:
796795
case LegacyHiddenComponent:
796+
case CacheComponent:
797797
return;
798-
case CacheComponent: {
799-
if (enableCache) {
800-
if (current !== null) {
801-
const oldCache: Cache | null = current.memoizedState;
802-
if (oldCache !== null) {
803-
const oldCacheProviders = oldCache.providers;
804-
if (oldCacheProviders) {
805-
oldCacheProviders.delete(current);
806-
oldCacheProviders.delete(finishedWork);
807-
}
808-
}
809-
}
810-
const newCache: Cache | null = finishedWork.memoizedState;
811-
if (newCache !== null) {
812-
const newCacheProviders = newCache.providers;
813-
if (newCacheProviders === null) {
814-
newCache.providers = new Set([finishedWork]);
815-
} else {
816-
newCacheProviders.add(finishedWork);
817-
}
818-
}
819-
}
820-
return;
821-
}
822798
}
823799
invariant(
824800
false,

packages/react-reconciler/src/ReactFiberCompleteWork.new.js

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type {
2828
} from './ReactFiberSuspenseComponent.new';
2929
import type {SuspenseContext} from './ReactFiberSuspenseContext.new';
3030
import type {OffscreenState} from './ReactFiberOffscreenComponent';
31+
import type {CacheInstance} from './ReactFiberCacheComponent';
3132

3233
import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new';
3334

@@ -1488,27 +1489,11 @@ function completeWork(
14881489
}
14891490
case CacheComponent: {
14901491
if (enableCache) {
1491-
// If the cache provided by this boundary has changed, schedule an
1492-
// effect to add this component to the cache's providers, and to remove
1493-
// it from the old cache.
1494-
// TODO: Schedule for Passive phase
1495-
const ownCache: Cache | null = workInProgress.memoizedState;
1496-
if (current === null) {
1497-
if (ownCache !== null) {
1498-
// This is a cache provider.
1499-
popProvider(CacheContext, workInProgress);
1500-
// Set up a refresh subscription.
1501-
workInProgress.flags |= Update;
1502-
}
1503-
} else {
1504-
if (ownCache !== null) {
1505-
// This is a cache provider.
1506-
popProvider(CacheContext, workInProgress);
1507-
}
1508-
if (ownCache !== current.memoizedState) {
1509-
// Cache changed. Create or update a refresh subscription.
1510-
workInProgress.flags |= Update;
1511-
}
1492+
const ownCacheInstance: CacheInstance | null =
1493+
workInProgress.memoizedState;
1494+
if (ownCacheInstance !== null) {
1495+
// This is a cache provider.
1496+
popProvider(CacheContext, workInProgress);
15121497
}
15131498
bubbleProperties(workInProgress);
15141499
return null;

0 commit comments

Comments
 (0)