Skip to content

Commit

Permalink
Remove client caching from cache() API (#27977)
Browse files Browse the repository at this point in the history
We haven't yet decided how we want `cache` to work on the client. The
lifetime of the cache is more complex than on the server, where it only
has to live as long as a single request.

Since it's more important to ship this on the server, we're removing the
existing behavior from the client for now. On the client (i.e. not a
Server Components environment) `cache` will have not have any caching
behavior. `cache(fn)` will return the function as-is.

We intend to implement client caching in a future major release. In the
meantime, it's only exposed as an API so that Shared Components can use
per-request caching on the server without breaking on the client.

DiffTrain build for [5c60736](5c60736)
  • Loading branch information
acdlite committed Jan 17, 2024
1 parent 0cd227f commit a2531fa
Show file tree
Hide file tree
Showing 9 changed files with 43 additions and 383 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f16344ea6db5bcc108de80dbc39a41ec28e8210d
5c607369ceebe56d85175df84b7b6ad58dd25e1f
118 changes: 15 additions & 103 deletions compiled/facebook-www/React-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ if (__DEV__) {
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
}
var ReactVersion = "18.3.0-www-classic-28af9135";
var ReactVersion = "18.3.0-www-classic-dd3d212c";

// ATTENTION
// When adding new symbols to this file,
Expand Down Expand Up @@ -2777,110 +2777,22 @@ if (__DEV__) {
return elementType;
}

var UNTERMINATED = 0;
var TERMINATED = 1;
var ERRORED = 2;

function createCacheRoot() {
return new WeakMap();
}

function createCacheNode() {
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.
};
}

function cache(fn) {
// On the client (i.e. not a Server Components environment) `cache` has
// no caching behavior. We just return the function as-is.
//
// We intend to implement client caching in a future major release. In the
// meantime, it's only exposed as an API so that Shared Components can use
// per-request caching on the server without breaking on the client. But it
// does mean they need to be aware of the behavioral difference.
//
// The rest of the behavior is the same as the server implementation — it
// returns a new reference, extra properties like `displayName` are not
// preserved, the length of the new function is 0, etc. That way apps can't
// accidentally depend on those details.
return function () {
var dispatcher = ReactCurrentCache.current;

if (!dispatcher) {
// If there is no dispatcher, then we treat this as not being cached.
// $FlowFixMe[incompatible-call]: We don't want to use rest arguments since we transpile the code.
return fn.apply(null, arguments);
}

var fnMap = dispatcher.getCacheForType(createCacheRoot);
var fnNode = fnMap.get(fn);
var cacheNode;

if (fnNode === undefined) {
cacheNode = createCacheNode();
fnMap.set(fn, cacheNode);
} else {
cacheNode = fnNode;
}

for (var i = 0, l = arguments.length; i < l; i++) {
var arg = arguments[i];

if (
typeof arg === "function" ||
(typeof arg === "object" && arg !== null)
) {
// Objects go into a WeakMap
var objectCache = cacheNode.o;

if (objectCache === null) {
cacheNode.o = objectCache = new WeakMap();
}

var objectNode = objectCache.get(arg);

if (objectNode === undefined) {
cacheNode = createCacheNode();
objectCache.set(arg, cacheNode);
} else {
cacheNode = objectNode;
}
} else {
// Primitives go into a regular Map
var primitiveCache = cacheNode.p;

if (primitiveCache === null) {
cacheNode.p = primitiveCache = new Map();
}

var 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[incompatible-call]: We don't want to use rest arguments since we transpile the code.
var result = fn.apply(null, arguments);
var terminatedNode = cacheNode;
terminatedNode.s = TERMINATED;
terminatedNode.v = result;
return result;
} catch (error) {
// We store the first error that's thrown and rethrow it.
var erroredNode = cacheNode;
erroredNode.s = ERRORED;
erroredNode.v = error;
throw error;
}
// $FlowFixMe[incompatible-call]: We don't want to use rest arguments since we transpile the code.
return fn.apply(null, arguments);
};
}

Expand Down
118 changes: 15 additions & 103 deletions compiled/facebook-www/React-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ if (__DEV__) {
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
}
var ReactVersion = "18.3.0-www-modern-b277b01a";
var ReactVersion = "18.3.0-www-modern-50d838b5";

// ATTENTION
// When adding new symbols to this file,
Expand Down Expand Up @@ -2742,110 +2742,22 @@ if (__DEV__) {
return elementType;
}

var UNTERMINATED = 0;
var TERMINATED = 1;
var ERRORED = 2;

function createCacheRoot() {
return new WeakMap();
}

function createCacheNode() {
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.
};
}

function cache(fn) {
// On the client (i.e. not a Server Components environment) `cache` has
// no caching behavior. We just return the function as-is.
//
// We intend to implement client caching in a future major release. In the
// meantime, it's only exposed as an API so that Shared Components can use
// per-request caching on the server without breaking on the client. But it
// does mean they need to be aware of the behavioral difference.
//
// The rest of the behavior is the same as the server implementation — it
// returns a new reference, extra properties like `displayName` are not
// preserved, the length of the new function is 0, etc. That way apps can't
// accidentally depend on those details.
return function () {
var dispatcher = ReactCurrentCache.current;

if (!dispatcher) {
// If there is no dispatcher, then we treat this as not being cached.
// $FlowFixMe[incompatible-call]: We don't want to use rest arguments since we transpile the code.
return fn.apply(null, arguments);
}

var fnMap = dispatcher.getCacheForType(createCacheRoot);
var fnNode = fnMap.get(fn);
var cacheNode;

if (fnNode === undefined) {
cacheNode = createCacheNode();
fnMap.set(fn, cacheNode);
} else {
cacheNode = fnNode;
}

for (var i = 0, l = arguments.length; i < l; i++) {
var arg = arguments[i];

if (
typeof arg === "function" ||
(typeof arg === "object" && arg !== null)
) {
// Objects go into a WeakMap
var objectCache = cacheNode.o;

if (objectCache === null) {
cacheNode.o = objectCache = new WeakMap();
}

var objectNode = objectCache.get(arg);

if (objectNode === undefined) {
cacheNode = createCacheNode();
objectCache.set(arg, cacheNode);
} else {
cacheNode = objectNode;
}
} else {
// Primitives go into a regular Map
var primitiveCache = cacheNode.p;

if (primitiveCache === null) {
cacheNode.p = primitiveCache = new Map();
}

var 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[incompatible-call]: We don't want to use rest arguments since we transpile the code.
var result = fn.apply(null, arguments);
var terminatedNode = cacheNode;
terminatedNode.s = TERMINATED;
terminatedNode.v = result;
return result;
} catch (error) {
// We store the first error that's thrown and rethrow it.
var erroredNode = cacheNode;
erroredNode.s = ERRORED;
erroredNode.v = error;
throw error;
}
// $FlowFixMe[incompatible-call]: We don't want to use rest arguments since we transpile the code.
return fn.apply(null, arguments);
};
}

Expand Down
45 changes: 2 additions & 43 deletions compiled/facebook-www/React-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,6 @@ function lazyInitializer(payload) {
if (1 === payload._status) return payload._result.default;
throw payload._result;
}
function createCacheRoot() {
return new WeakMap();
}
function createCacheNode() {
return { s: 0, v: void 0, o: null, p: null };
}
var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner,
RESERVED_PROPS = { key: !0, ref: !0, __self: !0, __source: !0 };
function jsx$1(type, config, maybeKey) {
Expand Down Expand Up @@ -355,42 +349,7 @@ exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED =
ReactSharedInternals;
exports.cache = function (fn) {
return function () {
var dispatcher = ReactCurrentCache.current;
if (!dispatcher) return fn.apply(null, arguments);
var fnMap = dispatcher.getCacheForType(createCacheRoot);
dispatcher = fnMap.get(fn);
void 0 === dispatcher &&
((dispatcher = createCacheNode()), fnMap.set(fn, dispatcher));
fnMap = 0;
for (var l = arguments.length; fnMap < l; fnMap++) {
var arg = arguments[fnMap];
if (
"function" === typeof arg ||
("object" === typeof arg && null !== arg)
) {
var objectCache = dispatcher.o;
null === objectCache && (dispatcher.o = objectCache = new WeakMap());
dispatcher = objectCache.get(arg);
void 0 === dispatcher &&
((dispatcher = createCacheNode()), objectCache.set(arg, dispatcher));
} else
(objectCache = dispatcher.p),
null === objectCache && (dispatcher.p = objectCache = new Map()),
(dispatcher = objectCache.get(arg)),
void 0 === dispatcher &&
((dispatcher = createCacheNode()),
objectCache.set(arg, dispatcher));
}
if (1 === dispatcher.s) return dispatcher.v;
if (2 === dispatcher.s) throw dispatcher.v;
try {
var result = fn.apply(null, arguments);
fnMap = dispatcher;
fnMap.s = 1;
return (fnMap.v = result);
} catch (error) {
throw ((result = dispatcher), (result.s = 2), (result.v = error), error);
}
return fn.apply(null, arguments);
};
};
exports.cloneElement = function (element, config, children) {
Expand Down Expand Up @@ -587,4 +546,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-www-classic-7998f47a";
exports.version = "18.3.0-www-classic-67c6907a";
45 changes: 2 additions & 43 deletions compiled/facebook-www/React-prod.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,6 @@ function lazyInitializer(payload) {
if (1 === payload._status) return payload._result.default;
throw payload._result;
}
function createCacheRoot() {
return new WeakMap();
}
function createCacheNode() {
return { s: 0, v: void 0, o: null, p: null };
}
var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner,
RESERVED_PROPS = { key: !0, ref: !0, __self: !0, __source: !0 };
function jsx$1(type, config, maybeKey) {
Expand Down Expand Up @@ -322,42 +316,7 @@ exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED =
ReactSharedInternals;
exports.cache = function (fn) {
return function () {
var dispatcher = ReactCurrentCache.current;
if (!dispatcher) return fn.apply(null, arguments);
var fnMap = dispatcher.getCacheForType(createCacheRoot);
dispatcher = fnMap.get(fn);
void 0 === dispatcher &&
((dispatcher = createCacheNode()), fnMap.set(fn, dispatcher));
fnMap = 0;
for (var l = arguments.length; fnMap < l; fnMap++) {
var arg = arguments[fnMap];
if (
"function" === typeof arg ||
("object" === typeof arg && null !== arg)
) {
var objectCache = dispatcher.o;
null === objectCache && (dispatcher.o = objectCache = new WeakMap());
dispatcher = objectCache.get(arg);
void 0 === dispatcher &&
((dispatcher = createCacheNode()), objectCache.set(arg, dispatcher));
} else
(objectCache = dispatcher.p),
null === objectCache && (dispatcher.p = objectCache = new Map()),
(dispatcher = objectCache.get(arg)),
void 0 === dispatcher &&
((dispatcher = createCacheNode()),
objectCache.set(arg, dispatcher));
}
if (1 === dispatcher.s) return dispatcher.v;
if (2 === dispatcher.s) throw dispatcher.v;
try {
var result = fn.apply(null, arguments);
fnMap = dispatcher;
fnMap.s = 1;
return (fnMap.v = result);
} catch (error) {
throw ((result = dispatcher), (result.s = 2), (result.v = error), error);
}
return fn.apply(null, arguments);
};
};
exports.cloneElement = function (element, config, children) {
Expand Down Expand Up @@ -579,4 +538,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactCurrentDispatcher.current.useTransition();
};
exports.version = "18.3.0-www-modern-cd8a63c4";
exports.version = "18.3.0-www-modern-14d3332b";

0 comments on commit a2531fa

Please sign in to comment.