Skip to content

Commit

Permalink
fix(useLoader): localize url caching per loader
Browse files Browse the repository at this point in the history
  • Loading branch information
CodyJasonBennett committed Apr 28, 2024
1 parent 0d8d270 commit 97c969f
Showing 1 changed file with 29 additions and 14 deletions.
43 changes: 29 additions & 14 deletions packages/fiber/src/core/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,15 @@ export type LoaderResult<T> = T extends { scene: THREE.Object3D } ? T & ObjectMa
export type Extensions<T> = (loader: Loader<T>) => void

const memoizedLoaders = new WeakMap<LoaderProto<any>, Loader<any>>()
const memoizedResults = new WeakMap<Loader<unknown>, Map<string, Promise<unknown>>>()

const isConstructor = <T,>(value: unknown): value is LoaderProto<T> =>
typeof value === 'function' && value?.prototype?.constructor === value

function loadingFn<T>(extensions?: Extensions<T>, onProgress?: (event: ProgressEvent) => void) {
return async function (Proto: Loader<T> | LoaderProto<T>, ...input: string[]) {
let loader: Loader<any>

return async function (Proto: Loader<T> | LoaderProto<T>, ...urls: string[]) {
// Construct and cache loader if constructor was passed
let loader!: Loader<T>
if (isConstructor(Proto)) {
loader = memoizedLoaders.get(Proto)!
if (!loader) {
Expand All @@ -110,22 +110,37 @@ function loadingFn<T>(extensions?: Extensions<T>, onProgress?: (event: ProgressE
loader = Proto
}

// Prime per-loader cache
let results: Map<string, Promise<unknown>> = memoizedResults.get(loader)!
if (!results) {
results = new Map()
memoizedResults.set(loader, results)
}

// Apply loader extensions
if (extensions) extensions(loader)

// Go through the urls and load them
return Promise.all(
input.map(
(input) =>
new Promise<LoaderResult<T>>((res, reject) =>
loader.load(
input,
(data) => res(isObject3D(data?.scene) ? Object.assign(data, buildGraph(data.scene)) : data),
onProgress,
(error) => reject(new Error(`Could not load ${input}: ${(error as ErrorEvent)?.message}`)),
),
),
),
urls.map((url) => {
// Load url and cache the result on first try
if (!results.has(url)) {
try {
results.set(
url,
loader
.loadAsync(url, onProgress)
.then((data) => (isObject3D(data?.scene) ? Object.assign(data, buildGraph(data.scene)) : data)),
)
} catch (error) {
// TODO: merge errors for multiple urls
throw new Error(`Could not load ${url}: ${(error as ErrorEvent)?.message}`)
}
}

// Return cached pending or completed result
return results.get(url)
}),
)
}
}
Expand Down

0 comments on commit 97c969f

Please sign in to comment.