diff --git a/packages/react-reconciler/src/__tests__/ReactCacheElement-test.js b/packages/react-reconciler/src/__tests__/ReactCacheElement-test.js
index ea84a0ae84ef5..6bd771fd48ea8 100644
--- a/packages/react-reconciler/src/__tests__/ReactCacheElement-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactCacheElement-test.js
@@ -2,6 +2,7 @@ let React;
let ReactNoop;
let Cache;
let getCacheSignal;
+let getCacheForType;
let Scheduler;
let assertLog;
let act;
@@ -10,13 +11,11 @@ let Activity;
let useCacheRefresh;
let startTransition;
let useState;
-let cache;
-let getTextCache;
let textCaches;
let seededCache;
-describe('ReactCache', () => {
+describe('ReactCacheElement', () => {
beforeEach(() => {
jest.resetModules();
@@ -26,9 +25,9 @@ describe('ReactCache', () => {
Scheduler = require('scheduler');
act = require('internal-test-utils').act;
Suspense = React.Suspense;
- cache = React.cache;
Activity = React.unstable_Activity;
getCacheSignal = React.unstable_getCacheSignal;
+ getCacheForType = React.unstable_getCacheForType;
useCacheRefresh = React.unstable_useCacheRefresh;
startTransition = React.startTransition;
useState = React.useState;
@@ -38,59 +37,57 @@ describe('ReactCache', () => {
textCaches = [];
seededCache = null;
+ });
- if (gate(flags => flags.enableCache)) {
- getTextCache = cache(() => {
- if (seededCache !== null) {
- // Trick to seed a cache before it exists.
- // TODO: Need a built-in API to seed data before the initial render (i.e.
- // not a refresh because nothing has mounted yet).
- const textCache = seededCache;
- seededCache = null;
- return textCache;
- }
-
- const data = new Map();
- const version = textCaches.length + 1;
- const textCache = {
- version,
- data,
- resolve(text) {
- const record = data.get(text);
- if (record === undefined) {
- const newRecord = {
- status: 'resolved',
- value: text,
- cleanupScheduled: false,
- };
- data.set(text, newRecord);
- } else if (record.status === 'pending') {
- record.value.resolve();
- }
- },
- reject(text, error) {
- const record = data.get(text);
- if (record === undefined) {
- const newRecord = {
- status: 'rejected',
- value: error,
- cleanupScheduled: false,
- };
- data.set(text, newRecord);
- } else if (record.status === 'pending') {
- record.value.reject();
- }
- },
- };
- textCaches.push(textCache);
- return textCache;
- });
+ function createTextCache() {
+ if (seededCache !== null) {
+ // Trick to seed a cache before it exists.
+ // TODO: Need a built-in API to seed data before the initial render (i.e.
+ // not a refresh because nothing has mounted yet).
+ const textCache = seededCache;
+ seededCache = null;
+ return textCache;
}
- });
+
+ const data = new Map();
+ const version = textCaches.length + 1;
+ const textCache = {
+ version,
+ data,
+ resolve(text) {
+ const record = data.get(text);
+ if (record === undefined) {
+ const newRecord = {
+ status: 'resolved',
+ value: text,
+ cleanupScheduled: false,
+ };
+ data.set(text, newRecord);
+ } else if (record.status === 'pending') {
+ record.value.resolve();
+ }
+ },
+ reject(text, error) {
+ const record = data.get(text);
+ if (record === undefined) {
+ const newRecord = {
+ status: 'rejected',
+ value: error,
+ cleanupScheduled: false,
+ };
+ data.set(text, newRecord);
+ } else if (record.status === 'pending') {
+ record.value.reject();
+ }
+ },
+ };
+ textCaches.push(textCache);
+ return textCache;
+ }
function readText(text) {
const signal = getCacheSignal ? getCacheSignal() : null;
- const textCache = getTextCache();
+ const textCache = getCacheForType(createTextCache);
const record = textCache.data.get(text);
if (record !== undefined) {
if (!record.cleanupScheduled) {
@@ -167,7 +164,7 @@ describe('ReactCache', () => {
function seedNextTextCache(text) {
if (seededCache === null) {
- seededCache = getTextCache();
+ seededCache = createTextCache();
}
seededCache.resolve(text);
}
@@ -182,7 +179,7 @@ describe('ReactCache', () => {
}
}
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('render Cache component', async () => {
const root = ReactNoop.createRoot();
await act(() => {
@@ -191,7 +188,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Hi');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('mount new data', async () => {
const root = ReactNoop.createRoot();
await act(() => {
@@ -220,7 +217,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCache
+ // @gate enableCacheElement
test('root acts as implicit cache boundary', async () => {
const root = ReactNoop.createRoot();
await act(() => {
@@ -247,7 +244,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('multiple new Cache boundaries in the same mount share the same, fresh root cache', async () => {
function App() {
return (
@@ -290,7 +287,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('multiple new Cache boundaries in the same update share the same, fresh cache', async () => {
function App({showMore}) {
return showMore ? (
@@ -342,7 +339,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test(
'nested cache boundaries share the same cache as the root during ' +
'the initial render',
@@ -382,7 +379,7 @@ describe('ReactCache', () => {
},
);
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('new content inside an existing Cache boundary should re-use already cached data', async () => {
function App({showMore}) {
return (
@@ -426,7 +423,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('a new Cache boundary uses fresh cache', async () => {
// The only difference from the previous test is that the "Show More"
// content is wrapped in a nested boundary
@@ -484,7 +481,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('inner/outer cache boundaries uses the same cache instance on initial render', async () => {
const root = ReactNoop.createRoot();
@@ -566,7 +563,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('inner/ outer cache boundaries added in the same update use the same cache instance', async () => {
const root = ReactNoop.createRoot();
@@ -655,7 +652,6 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCache
test('refresh a cache boundary', async () => {
let refresh;
function App() {
@@ -705,7 +701,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('refresh the root cache', async () => {
let refresh;
function App() {
@@ -753,7 +749,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('refresh the root cache without a transition', async () => {
let refresh;
function App() {
@@ -808,20 +804,11 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('refresh a cache with seed data', async () => {
- let refreshWithSeed;
+ let refresh;
function App() {
- const refresh = useCacheRefresh();
- const [seed, setSeed] = useState({fn: null});
- if (seed.fn) {
- seed.fn();
- seed.fn = null;
- }
- refreshWithSeed = fn => {
- setSeed({fn});
- refresh();
- };
+ refresh = useCacheRefresh();
return ;
}
@@ -851,12 +838,11 @@ describe('ReactCache', () => {
// server mutation.
// TODO: Seeding multiple typed textCaches. Should work by calling `refresh`
// multiple times with different key/value pairs
- startTransition(() =>
- refreshWithSeed(() => {
- const textCache = getTextCache();
- textCache.resolve('A');
- }),
- );
+ startTransition(() => {
+ const textCache = createTextCache();
+ textCache.resolve('A');
+ startTransition(() => refresh(createTextCache, textCache));
+ });
});
// The root should re-render without a cache miss.
// The cache is not cleared up yet, since it's still reference by the root
@@ -871,7 +857,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('refreshing a parent cache also refreshes its children', async () => {
let refreshShell;
function RefreshShell() {
@@ -946,7 +932,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test(
'refreshing a cache boundary does not refresh the other boundaries ' +
'that mounted at the same time (i.e. the ones that share the same cache)',
@@ -1027,7 +1013,7 @@ describe('ReactCache', () => {
},
);
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test(
'mount a new Cache boundary in a sibling while simultaneously ' +
'resolving a Suspense boundary',
@@ -1096,7 +1082,7 @@ describe('ReactCache', () => {
},
);
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('cache pool is cleared once transitions that depend on it commit their shell', async () => {
function Child({text}) {
return (
@@ -1189,7 +1175,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('cache pool is not cleared by arbitrary commits', async () => {
function App() {
return (
@@ -1268,7 +1254,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('cache boundary uses a fresh cache when its key changes', async () => {
const root = ReactNoop.createRoot();
seedNextTextCache('A');
@@ -1307,7 +1293,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('overlapping transitions after an initial mount use the same fresh cache', async () => {
const root = ReactNoop.createRoot();
await act(() => {
@@ -1375,7 +1361,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('overlapping updates after an initial mount use the same fresh cache', async () => {
const root = ReactNoop.createRoot();
await act(() => {
@@ -1438,7 +1424,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test('cleans up cache only used in an aborted transition', async () => {
const root = ReactNoop.createRoot();
seedNextTextCache('A');
@@ -1493,7 +1479,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test.skip('if a root cache refresh never commits its fresh cache is released', async () => {
const root = ReactNoop.createRoot();
let refresh;
@@ -1535,7 +1521,7 @@ describe('ReactCache', () => {
expect(root).toMatchRenderedOutput('Bye!');
});
- // @gate enableCacheElement && enableCache
+ // @gate enableCacheElement
test.skip('if a cache boundary refresh never commits its fresh cache is released', async () => {
const root = ReactNoop.createRoot();
let refresh;
diff --git a/packages/react-reconciler/src/__tests__/ReactUse-test.js b/packages/react-reconciler/src/__tests__/ReactUse-test.js
index 7f96506260d1b..fd49f8c8d784f 100644
--- a/packages/react-reconciler/src/__tests__/ReactUse-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactUse-test.js
@@ -11,7 +11,6 @@ let useMemo;
let useEffect;
let Suspense;
let startTransition;
-let cache;
let pendingTextRequests;
let waitFor;
let waitForPaint;
@@ -34,7 +33,6 @@ describe('ReactUse', () => {
useEffect = React.useEffect;
Suspense = React.Suspense;
startTransition = React.startTransition;
- cache = React.cache;
const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
@@ -643,10 +641,10 @@ describe('ReactUse', () => {
});
test('when waiting for data to resolve, an update on a different root does not cause work to be dropped', async () => {
- const getCachedAsyncText = cache(getAsyncText);
+ const promise = getAsyncText('Hi');
function App() {
- return ;
+ return ;
}
const root1 = ReactNoop.createRoot();
@@ -998,39 +996,46 @@ describe('ReactUse', () => {
);
test('load multiple nested Suspense boundaries', async () => {
- const getCachedAsyncText = cache(getAsyncText);
+ const promiseA = getAsyncText('A');
+ const promiseB = getAsyncText('B');
+ const promiseC = getAsyncText('C');
+ assertLog([
+ 'Async text requested [A]',
+ 'Async text requested [B]',
+ 'Async text requested [C]',
+ ]);
- function AsyncText({text}) {
- return ;
+ function AsyncText({promise}) {
+ return ;
}
const root = ReactNoop.createRoot();
await act(() => {
root.render(
}>
-
+
}>
-
+
}>
-
+
,
);
});
- assertLog(['Async text requested [A]', '(Loading A...)']);
+ assertLog(['(Loading A...)']);
expect(root).toMatchRenderedOutput('(Loading A...)');
await act(() => {
resolveTextRequests('A');
});
- assertLog(['A', 'Async text requested [B]', '(Loading B...)']);
+ assertLog(['A', '(Loading B...)']);
expect(root).toMatchRenderedOutput('A(Loading B...)');
await act(() => {
resolveTextRequests('B');
});
- assertLog(['B', 'Async text requested [C]', '(Loading C...)']);
+ assertLog(['B', '(Loading C...)']);
expect(root).toMatchRenderedOutput('AB(Loading C...)');
await act(() => {
@@ -1584,34 +1589,38 @@ describe('ReactUse', () => {
});
test('regression test: updates while component is suspended should not be mistaken for render phase updates', async () => {
- const getCachedAsyncText = cache(getAsyncText);
+ const promiseA = getAsyncText('A');
+ const promiseB = getAsyncText('B');
+ const promiseC = getAsyncText('C');
+ assertLog([
+ 'Async text requested [A]',
+ 'Async text requested [B]',
+ 'Async text requested [C]',
+ ]);
let setState;
function App() {
- const [state, _setState] = useState('A');
+ const [state, _setState] = useState(promiseA);
setState = _setState;
- return ;
+ return ;
}
// Initial render
const root = ReactNoop.createRoot();
await act(() => root.render());
- assertLog(['Async text requested [A]']);
expect(root).toMatchRenderedOutput(null);
await act(() => resolveTextRequests('A'));
assertLog(['A']);
expect(root).toMatchRenderedOutput('A');
// Update to B. This will suspend.
- await act(() => startTransition(() => setState('B')));
- assertLog(['Async text requested [B]']);
+ await act(() => startTransition(() => setState(promiseB)));
expect(root).toMatchRenderedOutput('A');
// While B is suspended, update to C. This should immediately interrupt
// the render for B. In the regression, this update was mistakenly treated
// as a render phase update.
- ReactNoop.flushSync(() => setState('C'));
- assertLog(['Async text requested [C]']);
+ ReactNoop.flushSync(() => setState(promiseC));
// Finish rendering.
await act(() => resolveTextRequests('C'));