@shopify/react-async
Version:
Tools for creating powerful, asynchronously-loaded React components
86 lines (83 loc) • 2.31 kB
JavaScript
import { useState, useCallback, useContext } from 'react';
import { useServerEffect } from '@shopify/react-effect';
import { useMountedRef } from '@shopify/react-hooks';
import { AsyncAssetContext } from './context/assets.mjs';
function usePreload(...args) {
const [preloadable, options = {}] = args;
return preloadable.usePreload(options);
}
function usePrefetch(...args) {
const [prefetchable, options = {}] = args;
return prefetchable.usePrefetch(options);
}
function useKeepFresh(...args) {
const [keepFreshable, options = {}] = args;
return keepFreshable.useKeepFresh(options);
}
function useAsync(resolver, {
assets,
scripts = assets,
styles = assets,
immediate = true
} = {}) {
const [value, setValue] = useState(() => immediate || typeof window !== 'undefined' ? resolver.resolved : null);
const mounted = useMountedRef();
const load = useCallback(async () => {
if (value != null) {
return value;
}
try {
const resolved = await resolver.resolve();
if (mounted.current) {
// It's important to use the function form of setValue here.
// Resolved is going to be a function in most cases, since it's
// a React component. If you do not set it using the function form,
// React treats the component as the function that returns state,
// so it sets state with the result of manually calling the component
// (so, usually JSX).
setValue(() => resolved);
}
return resolved;
} catch (error) {
if (mounted.current) {
setValue(error);
}
return error;
}
}, [mounted, resolver, value]);
const {
id
} = resolver;
useAsyncAsset(id, {
scripts,
styles
});
return value instanceof Error ? {
id,
resolved: null,
error: value,
loading: false,
load
} : {
id,
resolved: value,
error: null,
loading: value == null,
load
};
}
function useAsyncAsset(id, {
scripts,
styles
} = {}) {
const async = useContext(AsyncAssetContext);
useServerEffect(() => {
if (async && id) {
async.markAsUsed(id, {
scripts,
styles
});
}
}, async === null || async === void 0 ? void 0 : async.effect);
}
export { useAsync, useAsyncAsset, useKeepFresh, usePrefetch, usePreload };