diff --git a/packages/react-reconciler/src/__tests__/ReactCache-test.js b/packages/react-reconciler/src/__tests__/ReactCache-test.js
index 6bf494b1e0ad9..970bf940f27cd 100644
--- a/packages/react-reconciler/src/__tests__/ReactCache-test.js
+++ b/packages/react-reconciler/src/__tests__/ReactCache-test.js
@@ -2,6 +2,7 @@ let React;
let ReactNoop;
let Cache;
let getCacheSignal;
+let getCacheForType;
let Scheduler;
let act;
let Suspense;
@@ -9,10 +10,8 @@ let Offscreen;
let useCacheRefresh;
let startTransition;
let useState;
-let cache;
-let getTextCache;
-let textCaches;
+let caches;
let seededCache;
describe('ReactCache', () => {
@@ -25,68 +24,66 @@ describe('ReactCache', () => {
Scheduler = require('scheduler');
act = require('jest-react').act;
Suspense = React.Suspense;
- cache = React.experimental_cache;
Offscreen = React.unstable_Offscreen;
getCacheSignal = React.unstable_getCacheSignal;
+ getCacheForType = React.unstable_getCacheForType;
useCacheRefresh = React.unstable_useCacheRefresh;
startTransition = React.startTransition;
useState = React.useState;
- textCaches = [];
+ caches = [];
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 cache = seededCache;
+ seededCache = null;
+ return cache;
}
- });
+
+ const data = new Map();
+ const version = caches.length + 1;
+ const cache = {
+ 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();
+ }
+ },
+ };
+ caches.push(cache);
+ return cache;
+ }
function readText(text) {
const signal = getCacheSignal();
- const textCache = getTextCache();
+ const textCache = getCacheForType(createTextCache);
const record = textCache.data.get(text);
if (record !== undefined) {
if (!record.cleanupScheduled) {
@@ -163,18 +160,18 @@ describe('ReactCache', () => {
function seedNextTextCache(text) {
if (seededCache === null) {
- seededCache = getTextCache();
+ seededCache = createTextCache();
}
seededCache.resolve(text);
}
function resolveMostRecentTextCache(text) {
- if (textCaches.length === 0) {
+ if (caches.length === 0) {
throw Error('Cache does not exist.');
} else {
// Resolve the most recently created cache. An older cache can by
- // resolved with `textCaches[index].resolve(text)`.
- textCaches[textCaches.length - 1].resolve(text);
+ // resolved with `caches[index].resolve(text)`.
+ caches[caches.length - 1].resolve(text);
}
}
@@ -818,18 +815,9 @@ describe('ReactCache', () => {
// @gate experimental || www
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 ;
}
@@ -857,14 +845,11 @@ describe('ReactCache', () => {
await act(async () => {
// Refresh the cache with seeded data, like you would receive from a
// server mutation.
- // TODO: Seeding multiple typed textCaches. Should work by calling `refresh`
+ // TODO: Seeding multiple typed caches. Should work by calling `refresh`
// multiple times with different key/value pairs
- startTransition(() =>
- refreshWithSeed(() => {
- const textCache = getTextCache();
- textCache.resolve('A');
- }),
- );
+ const cache = createTextCache();
+ cache.resolve('A');
+ startTransition(() => refresh(createTextCache, cache));
});
// The root should re-render without a cache miss.
// The cache is not cleared up yet, since it's still reference by the root
@@ -1639,152 +1624,4 @@ describe('ReactCache', () => {
expect(Scheduler).toHaveYielded(['More']);
expect(root).toMatchRenderedOutput(
More
);
});
-
- // @gate enableCache
- it('cache objects and primitive arguments and a mix of them', async () => {
- const root = ReactNoop.createRoot();
- const types = cache((a, b) => ({a: typeof a, b: typeof b}));
- function Print({a, b}) {
- return types(a, b).a + ' ' + types(a, b).b + ' ';
- }
- function Same({a, b}) {
- const x = types(a, b);
- const y = types(a, b);
- return (x === y).toString() + ' ';
- }
- function FlippedOrder({a, b}) {
- return (types(a, b) === types(b, a)).toString() + ' ';
- }
- function FewerArgs({a, b}) {
- return (types(a, b) === types(a)).toString() + ' ';
- }
- function MoreArgs({a, b}) {
- return (types(a) === types(a, b)).toString() + ' ';
- }
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('string string true false false false ');
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('string object true false false false ');
- const obj = {};
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('string object true false false false ');
- const sameObj = {};
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('object object true true false false ');
- const objA = {};
- const objB = {};
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('object object true false false false ');
- const sameSymbol = Symbol();
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('symbol symbol true true false false ');
- const notANumber = +'nan';
- await act(async () => {
- root.render(
- <>
-
-
-
-
-
- >,
- );
- });
- expect(root).toMatchRenderedOutput('number number true false false false ');
- });
-
- // @gate enableCache
- it('cached functions that throw should cache the error', async () => {
- const root = ReactNoop.createRoot();
- const throws = cache(v => {
- throw new Error(v);
- });
- let x;
- let y;
- let z;
- function Test() {
- try {
- throws(1);
- } catch (e) {
- x = e;
- }
- try {
- throws(1);
- } catch (e) {
- y = e;
- }
- try {
- throws(2);
- } catch (e) {
- z = e;
- }
-
- return 'Blank';
- }
- await act(async () => {
- root.render();
- });
- expect(x).toBe(y);
- expect(z).not.toBe(x);
- });
});
diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js
index d7b56c551f194..6e90736a1c2fe 100644
--- a/packages/react/index.classic.fb.js
+++ b/packages/react/index.classic.fb.js
@@ -32,7 +32,6 @@ export {
isValidElement,
lazy,
memo,
- experimental_cache,
startTransition,
startTransition as unstable_startTransition, // TODO: Remove once call sights updated to startTransition
unstable_Cache,
diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js
index b296be4bb194c..9b40832086381 100644
--- a/packages/react/index.experimental.js
+++ b/packages/react/index.experimental.js
@@ -29,7 +29,6 @@ export {
isValidElement,
lazy,
memo,
- experimental_cache,
startTransition,
unstable_Cache,
unstable_DebugTracingMode,
diff --git a/packages/react/index.js b/packages/react/index.js
index 62c7d2f281404..145513fb93047 100644
--- a/packages/react/index.js
+++ b/packages/react/index.js
@@ -54,7 +54,6 @@ export {
isValidElement,
lazy,
memo,
- experimental_cache,
startTransition,
unstable_Cache,
unstable_DebugTracingMode,
diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js
index ba6a505d96920..0fc400a8940a0 100644
--- a/packages/react/index.modern.fb.js
+++ b/packages/react/index.modern.fb.js
@@ -31,7 +31,6 @@ export {
isValidElement,
lazy,
memo,
- experimental_cache,
startTransition,
startTransition as unstable_startTransition, // TODO: Remove once call sights updated to startTransition
unstable_Cache,
diff --git a/packages/react/src/React.js b/packages/react/src/React.js
index 3cb0d13a13a05..fe217ee406143 100644
--- a/packages/react/src/React.js
+++ b/packages/react/src/React.js
@@ -35,7 +35,6 @@ import {createContext} from './ReactContext';
import {lazy} from './ReactLazy';
import {forwardRef} from './ReactForwardRef';
import {memo} from './ReactMemo';
-import {cache} from './ReactCache';
import {
getCacheSignal,
getCacheForType,
@@ -104,7 +103,6 @@ export {
forwardRef,
lazy,
memo,
- cache as experimental_cache,
useCallback,
useContext,
useEffect,
diff --git a/packages/react/src/ReactCache.js b/packages/react/src/ReactCache.js
deleted file mode 100644
index b308ad9e16d24..0000000000000
--- a/packages/react/src/ReactCache.js
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @flow
- */
-
-import ReactCurrentCache from './ReactCurrentCache';
-
-const UNTERMINATED = 0;
-const TERMINATED = 1;
-const ERRORED = 2;
-
-type UnterminatedCacheNode = {
- s: 0,
- v: void,
- o: null | WeakMap>,
- p: null | Map>,
-};
-
-type TerminatedCacheNode = {
- s: 1,
- v: T,
- o: null | WeakMap>,
- p: null | Map>,
-};
-
-type ErroredCacheNode = {
- s: 2,
- v: mixed,
- o: null | WeakMap>,
- p: null | Map>,
-};
-
-type CacheNode =
- | TerminatedCacheNode
- | UnterminatedCacheNode
- | ErroredCacheNode;
-
-function createCacheRoot(): WeakMap> {
- return new WeakMap();
-}
-
-function createCacheNode(): CacheNode {
- return {
- s: UNTERMINATED, // status, represents whether the cached computation returned a value or threw an error
- v: undefined, // value, either the cached result or an error, depending on s
- o: null, // object cache, a WeakMap where non-primitive arguments are stored
- p: null, // primitive cache, a regular Map where primitive arguments are stored.
- };
-}
-
-export function cache, T>(fn: (...A) => T): (...A) => T {
- return function() {
- const dispatcher = ReactCurrentCache.current;
- if (!dispatcher) {
- // If there is no dispatcher, then we treat this as not being cached.
- // $FlowFixMe: We don't want to use rest arguments since we transpile the code.
- return fn.apply(null, arguments);
- }
- const fnMap = dispatcher.getCacheForType(createCacheRoot);
- const fnNode = fnMap.get(fn);
- let cacheNode: CacheNode;
- if (fnNode === undefined) {
- cacheNode = createCacheNode();
- fnMap.set(fn, cacheNode);
- } else {
- cacheNode = fnNode;
- }
- for (let i = 0, l = arguments.length; i < l; i++) {
- const arg = arguments[i];
- if (
- typeof arg === 'function' ||
- (typeof arg === 'object' && arg !== null)
- ) {
- // Objects go into a WeakMap
- let objectCache = cacheNode.o;
- if (objectCache === null) {
- cacheNode.o = objectCache = new WeakMap();
- }
- const objectNode = objectCache.get(arg);
- if (objectNode === undefined) {
- cacheNode = createCacheNode();
- objectCache.set(arg, cacheNode);
- } else {
- cacheNode = objectNode;
- }
- } else {
- // Primitives go into a regular Map
- let primitiveCache = cacheNode.p;
- if (primitiveCache === null) {
- cacheNode.p = primitiveCache = new Map();
- }
- const primitiveNode = primitiveCache.get(arg);
- if (primitiveNode === undefined) {
- cacheNode = createCacheNode();
- primitiveCache.set(arg, cacheNode);
- } else {
- cacheNode = primitiveNode;
- }
- }
- }
- if (cacheNode.s === TERMINATED) {
- return cacheNode.v;
- }
- if (cacheNode.s === ERRORED) {
- throw cacheNode.v;
- }
- try {
- // $FlowFixMe: We don't want to use rest arguments since we transpile the code.
- const result = fn.apply(null, arguments);
- const terminatedNode: TerminatedCacheNode = (cacheNode: any);
- terminatedNode.s = TERMINATED;
- terminatedNode.v = result;
- return result;
- } catch (error) {
- // We store the first error that's thrown and rethrow it.
- const erroredNode: ErroredCacheNode = (cacheNode: any);
- erroredNode.s = ERRORED;
- erroredNode.v = error;
- throw error;
- }
- };
-}
diff --git a/packages/react/src/ReactSharedSubset.experimental.js b/packages/react/src/ReactSharedSubset.experimental.js
index 1283c51a9ad53..cb6c746c9bce8 100644
--- a/packages/react/src/ReactSharedSubset.experimental.js
+++ b/packages/react/src/ReactSharedSubset.experimental.js
@@ -24,7 +24,6 @@ export {
isValidElement,
lazy,
memo,
- experimental_cache,
startTransition,
unstable_DebugTracingMode,
unstable_getCacheSignal,