Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
React concurrent asset loader
import { unstable_getCacheForType } from 'react';
import deepEqual from 'fast-deep-equal';
let hasArgs = (args) => (record) => deepEqual(args, record.args);
class Asset {
#loader;
#lifespan;
#key = () => [];
constructor (loader, lifespan = 0) {
this.#loader = loader;
this.#lifespan = lifespan;
}
#getCache () {
return unstable_getCacheForType(this.#key);
}
#createRecord (args, preload = false) {
let cache = this.#getCache();
for (let record of cache) {
if (deepEqual(args, record.args)) {
if (preload) return;
if (record.result) return record.result;
throw record.error || record.promise;
}
}
let lifespan = this.#lifespan;
let promise = this.#loader(...args)
.then((result) => (record.result = result))
.catch((error) => (record.error = error))
.finally(() => {
if (lifespan > 0) {
setTimeout(() => {
let index = cache.indexOf(record);
if (index !== -1) cache.splice(index, 1);
}, lifespan);
}
});
let record = {
args,
promise,
};
cache.push(record);
if (!preload) throw record.promise;
}
load (...args) {
return this.#createRecord(args);
}
preload (...args) {
this.#createRecord(args, true);
}
peek (...args) {
let cache = this.#getCache();
return cache.find(hasArgs(args))?.result;
}
clear (...args) {
let cache = this.#getCache();
if (!args.length) {
cache.splice(0, cache.length);
} else {
let index = cache.findIndex(hasArgs(args));
if (index !== -1) cache.splice(index, 1);
}
}
}
export function createAsset (loader, lifespan) {
return new Asset(loader, lifespan);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment