Skip to content

Commit

Permalink
feat: update useAsset to better support Suspense on Next.js
Browse files Browse the repository at this point in the history
Co-authored-by: Justin Walsh <contact.me@thejustinwalsh.com>
  • Loading branch information
trezy and thejustinwalsh committed Jul 12, 2024
1 parent 34e08ff commit de99be6
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 8 deletions.
53 changes: 45 additions & 8 deletions src/hooks/useAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,64 @@ import {
} from 'pixi.js';
import { getAssetKeyFromOptions } from '../helpers/getAssetKeyFromOptions.js';

/** @typedef {import('pixi.js').ProgressCallback} ProgressCallback */
/**
* @template T
* @typedef {import('pixi.js').UnresolvedAsset<T>} UnresolvedAsset
*/
/** @typedef {import('../typedefs/AssetRetryOptions.ts').AssetRetryOptions} AssetRetryOptions */
/** @typedef {import('../typedefs/AssetRetryState.ts').AssetRetryState} AssetRetryState */

/** @type {Map<import('pixi.js').UnresolvedAsset | string, AssetRetryState>} */
const errorCache = new Map();

/**
* Loads assets, returning a hash of assets once they're loaded.
*
* @template T
* @param {UnresolvedAsset<T> | string} options Asset options.
* @param {ProgressCallback} [onProgress] A function to be called when the asset loader reports loading progress.
* @param {(import('pixi.js').UnresolvedAsset<T> & AssetRetryOptions) | string} options Asset options.
* @param {import('pixi.js').ProgressCallback} [onProgress] A function to be called when the asset loader reports loading progress.
* @returns {T}
*/
export function useAsset(options, onProgress)
{
if (typeof window === 'undefined')
{
throw Object.assign(Error('`useAsset` will only run on the client.'), {
digest: 'BAILOUT_TO_CLIENT_SIDE_RENDERING',
});
}

const {
maxRetries = 3,
retryOnFailure = true,
} = typeof options !== 'string' ? options : {};

const assetKey = getAssetKeyFromOptions(options);

if (!Cache.has(assetKey))
{
throw Assets.load(options, onProgress);
let state = errorCache.get(options);

// Rethrow the cached error if we are not retrying on failure or have reached the max retries
if (state && (!retryOnFailure || state.retries > maxRetries))
{
throw state.error;
}

throw Assets
.load(options, onProgress)
.catch((error) =>
{
if (!state)
{
state = {
error,
retries: 0,
};
}

errorCache.set(options, {
...state,
error,
retries: state.retries + 1,
});
});
}

return Assets.get(assetKey);
Expand Down
8 changes: 8 additions & 0 deletions src/typedefs/AssetRetryOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface AssetRetryOptions
{
/** @description The maximum number of retries allowed before we give up on loading this asset. */
maxRetries?: number

/** @description Whether to try loading this asset again if it fails. */
retryOnFailure?: boolean
}
5 changes: 5 additions & 0 deletions src/typedefs/AssetRetryState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface AssetRetryState
{
error: Error
retries: number
}

0 comments on commit de99be6

Please sign in to comment.