revenge-plugins/shared/lib/lazy.ts

25 lines
1.1 KiB
TypeScript
Raw Normal View History

/**
* Wraps a metro lookup so it isn't run until first actually used, and retries on every call until
* it succeeds once (after which the result is cached forever). A plugin's top-level module code
* runs as soon as its bundle is required, which can be before Discord's own code has required an
* internal module it depends on - a one-shot findByX() at module scope can permanently cache
* undefined even though the real module becomes available moments later. Only meant for values
* used inside event handlers or plain (non-hook) render logic - wrapping something used as a
* React hook (e.g. passed to useContext) in this can violate the Rules of Hooks, since the
* resolved-or-not status is now allowed to change between renders of the same component instance.
*/
export function lazy<T>(resolve: () => T | null | undefined): () => T | undefined {
let cached: T | undefined;
let resolved = false;
return () => {
if (resolved) return cached;
const value = resolve();
if (value != null) {
cached = value;
resolved = true;
}
return value ?? undefined;
};
}