Skip to content

Commit c5e89a0

Browse files
committed
Add implicit root-level cache
If `getCacheForType` or `useRefresh` cannot find a parent <Cache />, they will access a top-level cache associated with the root. The behavior is effectively the same as if you wrapped the entire tree in a <Cache /> boundary.
1 parent b13ea9e commit c5e89a0

13 files changed

+180
-10
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,15 @@ function updateHostRoot(current, workInProgress, renderLanes) {
11041104
const nextState = workInProgress.memoizedState;
11051105
// Caution: React DevTools currently depends on this property
11061106
// being called "element".
1107+
1108+
if (enableCache) {
1109+
const nextCacheInstance: CacheInstance = nextState.cacheInstance;
1110+
pushProvider(workInProgress, CacheContext, nextCacheInstance);
1111+
if (nextCacheInstance !== prevState.cacheInstance) {
1112+
propagateCacheRefresh(workInProgress, renderLanes);
1113+
}
1114+
}
1115+
11071116
const nextChildren = nextState.element;
11081117
if (nextChildren === prevChildren) {
11091118
resetHydrationState();
@@ -3174,6 +3183,11 @@ function beginWork(
31743183
switch (workInProgress.tag) {
31753184
case HostRoot:
31763185
pushHostRootContext(workInProgress);
3186+
if (enableCache) {
3187+
const nextCacheInstance: CacheInstance =
3188+
current.memoizedState.cacheInstance;
3189+
pushProvider(workInProgress, CacheContext, nextCacheInstance);
3190+
}
31773191
resetHydrationState();
31783192
break;
31793193
case HostComponent:

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,15 @@ function updateHostRoot(current, workInProgress, renderLanes) {
11041104
const nextState = workInProgress.memoizedState;
11051105
// Caution: React DevTools currently depends on this property
11061106
// being called "element".
1107+
1108+
if (enableCache) {
1109+
const nextCacheInstance: CacheInstance = nextState.cacheInstance;
1110+
pushProvider(workInProgress, CacheContext, nextCacheInstance);
1111+
if (nextCacheInstance !== prevState.cacheInstance) {
1112+
propagateCacheRefresh(workInProgress, renderLanes);
1113+
}
1114+
}
1115+
11071116
const nextChildren = nextState.element;
11081117
if (nextChildren === prevChildren) {
11091118
resetHydrationState();
@@ -3174,6 +3183,11 @@ function beginWork(
31743183
switch (workInProgress.tag) {
31753184
case HostRoot:
31763185
pushHostRootContext(workInProgress);
3186+
if (enableCache) {
3187+
const nextCacheInstance: CacheInstance =
3188+
current.memoizedState.cacheInstance;
3189+
pushProvider(workInProgress, CacheContext, nextCacheInstance);
3190+
}
31773191
resetHydrationState();
31783192
break;
31793193
case HostComponent:

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ function completeWork(
813813
return null;
814814
}
815815
case HostRoot: {
816+
if (enableCache) {
817+
popProvider(CacheContext, workInProgress);
818+
}
816819
popHostContainer(workInProgress);
817820
popTopLevelLegacyContextObject(workInProgress);
818821
resetMutableSourceWorkInProgressVersions();

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ function completeWork(
813813
return null;
814814
}
815815
case HostRoot: {
816+
if (enableCache) {
817+
popProvider(CacheContext, workInProgress);
818+
}
816819
popHostContainer(workInProgress);
817820
popTopLevelLegacyContextObject(workInProgress);
818821
resetMutableSourceWorkInProgressVersions();

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
enableUseRefAccessWarning,
3232
} from 'shared/ReactFeatureFlags';
3333

34+
import {HostRoot} from './ReactWorkTags';
3435
import {NoMode, BlockingMode, DebugTracingMode} from './ReactTypeOfMode';
3536
import {
3637
NoLane,
@@ -94,6 +95,7 @@ import {getIsRendering} from './ReactCurrentFiber';
9495
import {logStateUpdateScheduled} from './DebugTracing';
9596
import {markStateUpdateScheduled} from './SchedulingProfiler';
9697
import {CacheContext} from './ReactFiberCacheComponent';
98+
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue.new';
9799

98100
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
99101

@@ -1741,12 +1743,31 @@ function refreshCache<T>(
17411743
try {
17421744
const eventTime = requestEventTime();
17431745
const lane = requestUpdateLane(provider);
1746+
// TODO: Does Cache work in legacy mode? Should decide and write a test.
17441747
const root = scheduleUpdateOnFiber(provider, lane, eventTime);
1748+
1749+
let seededCache = null;
17451750
if (seedKey !== null && seedKey !== undefined && root !== null) {
17461751
// TODO: Warn if wrong type
1747-
const seededCache = new Map([[seedKey, seedValue]]);
1752+
seededCache = new Map([[seedKey, seedValue]]);
17481753
transferCacheToSpawnedLane(root, seededCache, lane);
17491754
}
1755+
1756+
if (provider.tag === HostRoot) {
1757+
const refreshUpdate = createUpdate(eventTime, lane);
1758+
refreshUpdate.payload = {
1759+
cacheInstance: {
1760+
provider: provider,
1761+
cache:
1762+
// For the root cache, we won't bother to lazily initialize the
1763+
// map. Seed an empty one. This saves use the trouble of having
1764+
// to use an updater function. Maybe we should use this approach
1765+
// for non-root refreshes, too.
1766+
seededCache !== null ? seededCache : new Map(),
1767+
},
1768+
};
1769+
enqueueUpdate(provider, refreshUpdate);
1770+
}
17501771
} finally {
17511772
ReactCurrentBatchConfig.transition = prevTransition;
17521773
}
@@ -1869,9 +1890,7 @@ function getCacheForType<T>(resourceType: () => T): T {
18691890
const cacheInstance: CacheInstance | null = readContext(CacheContext);
18701891
invariant(
18711892
cacheInstance !== null,
1872-
'Tried to fetch data, but no cache was found. To fix, wrap your ' +
1873-
"component in a <Cache /> boundary. It doesn't need to be a direct " +
1874-
'parent; it can be anywhere in the ancestor path',
1893+
'Internal React error: Should always have a cache.',
18751894
);
18761895
let cache = cacheInstance.cache;
18771896
if (cache === null) {

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
enableDoubleInvokingEffects,
3333
} from 'shared/ReactFeatureFlags';
3434

35+
import {HostRoot} from './ReactWorkTags';
3536
import {
3637
NoMode,
3738
BlockingMode,
@@ -102,6 +103,7 @@ import {getIsRendering} from './ReactCurrentFiber';
102103
import {logStateUpdateScheduled} from './DebugTracing';
103104
import {markStateUpdateScheduled} from './SchedulingProfiler';
104105
import {CacheContext} from './ReactFiberCacheComponent';
106+
import {createUpdate, enqueueUpdate} from './ReactUpdateQueue.old';
105107

106108
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
107109

@@ -1812,12 +1814,31 @@ function refreshCache<T>(
18121814
try {
18131815
const eventTime = requestEventTime();
18141816
const lane = requestUpdateLane(provider);
1817+
// TODO: Does Cache work in legacy mode? Should decide and write a test.
18151818
const root = scheduleUpdateOnFiber(provider, lane, eventTime);
1819+
1820+
let seededCache = null;
18161821
if (seedKey !== null && seedKey !== undefined && root !== null) {
18171822
// TODO: Warn if wrong type
1818-
const seededCache = new Map([[seedKey, seedValue]]);
1823+
seededCache = new Map([[seedKey, seedValue]]);
18191824
transferCacheToSpawnedLane(root, seededCache, lane);
18201825
}
1826+
1827+
if (provider.tag === HostRoot) {
1828+
const refreshUpdate = createUpdate(eventTime, lane);
1829+
refreshUpdate.payload = {
1830+
cacheInstance: {
1831+
provider: provider,
1832+
cache:
1833+
// For the root cache, we won't bother to lazily initialize the
1834+
// map. Seed an empty one. This saves use the trouble of having
1835+
// to use an updater function. Maybe we should use this approach
1836+
// for non-root refreshes, too.
1837+
seededCache !== null ? seededCache : new Map(),
1838+
},
1839+
};
1840+
enqueueUpdate(provider, refreshUpdate);
1841+
}
18211842
} finally {
18221843
ReactCurrentBatchConfig.transition = prevTransition;
18231844
}
@@ -1940,9 +1961,7 @@ function getCacheForType<T>(resourceType: () => T): T {
19401961
const cacheInstance: CacheInstance | null = readContext(CacheContext);
19411962
invariant(
19421963
cacheInstance !== null,
1943-
'Tried to fetch data, but no cache was found. To fix, wrap your ' +
1944-
"component in a <Cache /> boundary. It doesn't need to be a direct " +
1945-
'parent; it can be anywhere in the ancestor path',
1964+
'Internal React error: Should always have a cache.',
19461965
);
19471966
let cache = cacheInstance.cache;
19481967
if (cache === null) {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ export function createFiberRoot(
103103
root.current = uninitializedFiber;
104104
uninitializedFiber.stateNode = root;
105105

106+
const initialState = {
107+
element: null,
108+
// For the root cache, we won't bother to lazily initialize the map. Seed an
109+
// empty one. This saves use the trouble of having to initialize in an
110+
// updater function.
111+
cacheInstance: {
112+
cache: new Map(),
113+
provider: uninitializedFiber,
114+
},
115+
};
116+
uninitializedFiber.memoizedState = initialState;
117+
106118
initializeUpdateQueue(uninitializedFiber);
107119

108120
return root;

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ export function createFiberRoot(
103103
root.current = uninitializedFiber;
104104
uninitializedFiber.stateNode = root;
105105

106+
const initialState = {
107+
element: null,
108+
// For the root cache, we won't bother to lazily initialize the map. Seed an
109+
// empty one. This saves use the trouble of having to initialize in an
110+
// updater function.
111+
cacheInstance: {
112+
cache: new Map(),
113+
provider: uninitializedFiber,
114+
},
115+
};
116+
uninitializedFiber.memoizedState = initialState;
117+
106118
initializeUpdateQueue(uninitializedFiber);
107119

108120
return root;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
7070
return null;
7171
}
7272
case HostRoot: {
73+
if (enableCache) {
74+
popProvider(CacheContext, workInProgress);
75+
}
7376
popHostContainer(workInProgress);
7477
popTopLevelLegacyContextObject(workInProgress);
7578
resetMutableSourceWorkInProgressVersions();
@@ -156,6 +159,9 @@ function unwindInterruptedWork(interruptedWork: Fiber) {
156159
break;
157160
}
158161
case HostRoot: {
162+
if (enableCache) {
163+
popProvider(CacheContext, interruptedWork);
164+
}
159165
popHostContainer(interruptedWork);
160166
popTopLevelLegacyContextObject(interruptedWork);
161167
resetMutableSourceWorkInProgressVersions();

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ function unwindWork(workInProgress: Fiber, renderLanes: Lanes) {
7070
return null;
7171
}
7272
case HostRoot: {
73+
if (enableCache) {
74+
popProvider(CacheContext, workInProgress);
75+
}
7376
popHostContainer(workInProgress);
7477
popTopLevelLegacyContextObject(workInProgress);
7578
resetMutableSourceWorkInProgressVersions();
@@ -156,6 +159,9 @@ function unwindInterruptedWork(interruptedWork: Fiber) {
156159
break;
157160
}
158161
case HostRoot: {
162+
if (enableCache) {
163+
popProvider(CacheContext, interruptedWork);
164+
}
159165
popHostContainer(interruptedWork);
160166
popTopLevelLegacyContextObject(interruptedWork);
161167
resetMutableSourceWorkInProgressVersions();

0 commit comments

Comments
 (0)