Skip to content

Instantly share code, notes, and snippets.

@ellemedit
Created August 14, 2024 02:22
Show Gist options
  • Save ellemedit/412afbf1fcf9e563bf0ce209bf751693 to your computer and use it in GitHub Desktop.
Save ellemedit/412afbf1fcf9e563bf0ce209bf751693 to your computer and use it in GitHub Desktop.
React cache like implementation
// copied from https://github.com/facebook/react/blob/8e60bacd08215bd23f0bf05dde407cd133885aa1/packages/react/src/ReactCacheImpl.js#L50
// but no react context
const rootCacheNode = new WeakMap<(...args: any[]) => any, CacheNode>();
const UNTERMINATED = 0;
const TERMINATED = 1;
const ERRORED = 2;
type CacheNode = {
o: WeakMap<Function | object, CacheNode> | null;
p: Map<string | number | null | void | symbol | boolean, CacheNode> | null;
v: unknown;
s: typeof UNTERMINATED | typeof TERMINATED | typeof ERRORED;
};
export function cache<T extends (...args: any[]) => any>(func: T): T {
// @ts-expect-error idk how to type this
return function (...args: Parameters<T>) {
if (!rootCacheNode.has(func)) {
rootCacheNode.set(func, createCacheNode());
}
let currentCacheNode = rootCacheNode.get(func)!;
for (const arg of args) {
if (
(typeof arg === "object" && arg !== null) ||
typeof arg === "function"
) {
if (!currentCacheNode.o) {
currentCacheNode.o = new WeakMap();
}
if (currentCacheNode.o.has(arg)) {
currentCacheNode = currentCacheNode.o.get(arg)!;
} else {
const cacheNode = createCacheNode();
currentCacheNode.o.set(arg, cacheNode);
currentCacheNode = cacheNode;
}
} else {
if (!currentCacheNode.p) {
currentCacheNode.p = new Map();
}
if (currentCacheNode.p.has(arg)) {
currentCacheNode = currentCacheNode.p.get(arg)!;
} else {
const cacheNode = createCacheNode();
currentCacheNode.p.set(arg, cacheNode);
currentCacheNode = cacheNode;
}
}
}
if (currentCacheNode.s === TERMINATED) {
return currentCacheNode.v;
}
if (currentCacheNode.s === ERRORED) {
throw currentCacheNode.v;
}
try {
const value = func.apply(null, args);
currentCacheNode.s = TERMINATED;
currentCacheNode.v = value;
return value;
} catch (error) {
currentCacheNode.s = ERRORED;
currentCacheNode.v = error;
throw error;
}
};
}
function createCacheNode(): CacheNode {
return {
o: null,
p: null,
v: null,
s: UNTERMINATED,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment