@shopify/react-async
Version:
Tools for creating powerful, asynchronously-loaded React components.
191 lines (190 loc) • 7.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importStar(require("react"));
var async_1 = require("@shopify/async");
var react_idle_1 = require("@shopify/react-idle");
var react_intersection_observer_1 = require("@shopify/react-intersection-observer");
var react_hydrate_1 = require("@shopify/react-hydrate");
var hooks_1 = require("./hooks");
var types_1 = require("./types");
function createAsyncComponent(_a) {
var id = _a.id, load = _a.load, defer = _a.defer, deferHydration = _a.deferHydration, displayName = _a.displayName, _b = _a.renderLoading, renderLoading = _b === void 0 ? noopRender : _b, _c = _a.renderError, renderError = _c === void 0 ? defaultRenderError : _c, _d = _a.usePreload, useCustomPreload = _d === void 0 ? noopUse : _d, _e = _a.usePrefetch, useCustomPrefetch = _e === void 0 ? noopUse : _e, _f = _a.useKeepFresh, useCustomKeepFresh = _f === void 0 ? noopUse : _f;
var resolver = async_1.createResolver({ id: id, load: load });
var componentName = displayName || displayNameFromId(resolver.id);
var deferred = defer != null;
var progressivelyHydrated = deferHydration != null;
var scriptTiming = deferred || progressivelyHydrated
? types_1.AssetTiming.CurrentPage
: types_1.AssetTiming.Immediate;
var stylesTiming = deferred
? types_1.AssetTiming.CurrentPage
: types_1.AssetTiming.Immediate;
function Async(props) {
var _a = hooks_1.useAsync(resolver, {
scripts: scriptTiming,
styles: stylesTiming,
immediate: !deferred,
}), Component = _a.resolved, load = _a.load, loading = _a.loading, error = _a.error;
var startedHydrated = react_1.useRef(react_hydrate_1.useHydrationManager().hydrated).current;
if (error) {
return renderError(error);
}
var loadingMarkup = null;
if (progressivelyHydrated && !startedHydrated) {
loadingMarkup = (react_1.default.createElement(Loader, { defer: deferHydration, load: load, props: props }));
}
else if (loading) {
loadingMarkup = react_1.default.createElement(Loader, { defer: defer, load: load, props: props });
}
var contentMarkup = null;
var rendered = Component ? react_1.default.createElement(Component, tslib_1.__assign({}, props)) : null;
if (progressivelyHydrated && !startedHydrated) {
contentMarkup = rendered ? (react_1.default.createElement(react_hydrate_1.Hydrator, { id: resolver.id }, rendered)) : (react_1.default.createElement(react_hydrate_1.Hydrator, { id: resolver.id }));
}
else if (loading) {
contentMarkup = renderLoading(props);
}
else {
contentMarkup = rendered;
}
return (react_1.default.createElement(react_1.default.Fragment, null,
contentMarkup,
loadingMarkup));
}
Async.displayName = "Async(" + componentName + ")";
function usePreload(props) {
var load = hooks_1.useAsync(resolver, {
assets: types_1.AssetTiming.NextPage,
}).load;
var customPreload = useCustomPreload(props);
return react_1.useCallback(function () {
load();
if (customPreload) {
customPreload();
}
}, [load, customPreload]);
}
function usePrefetch(props) {
var load = hooks_1.useAsync(resolver, {
assets: types_1.AssetTiming.NextPage,
}).load;
var customPrefetch = useCustomPrefetch(props);
return react_1.useCallback(function () {
load();
if (customPrefetch) {
customPrefetch();
}
}, [load, customPrefetch]);
}
function useKeepFresh(props) {
var load = hooks_1.useAsync(resolver, {
assets: types_1.AssetTiming.NextPage,
}).load;
var customKeepFresh = useCustomKeepFresh(props);
return react_1.useCallback(function () {
load();
if (customKeepFresh) {
customKeepFresh();
}
}, [load, customKeepFresh]);
}
function Preload(options) {
react_idle_1.useIdleCallback(usePreload(options));
return null;
}
Preload.displayName = "Async.Preload(" + displayName + ")";
function Prefetch(options) {
var prefetch = usePrefetch(options);
react_1.useEffect(function () {
prefetch();
}, [prefetch]);
return null;
}
Prefetch.displayName = "Async.Prefetch(" + displayName + ")";
function KeepFresh(options) {
react_idle_1.useIdleCallback(useKeepFresh(options));
return null;
}
KeepFresh.displayName = "Async.KeepFresh(" + displayName + ")";
var FinalComponent = Async;
Reflect.defineProperty(FinalComponent, 'resolver', {
value: resolver,
writable: false,
});
Reflect.defineProperty(FinalComponent, 'Preload', {
value: Preload,
writable: false,
});
Reflect.defineProperty(FinalComponent, 'Prefetch', {
value: Prefetch,
writable: false,
});
Reflect.defineProperty(FinalComponent, 'KeepFresh', {
value: KeepFresh,
writable: false,
});
Reflect.defineProperty(FinalComponent, 'usePreload', {
value: usePreload,
writable: false,
});
Reflect.defineProperty(FinalComponent, 'usePrefetch', {
value: usePrefetch,
writable: false,
});
Reflect.defineProperty(FinalComponent, 'useKeepFresh', {
value: useKeepFresh,
writable: false,
});
return FinalComponent;
}
exports.createAsyncComponent = createAsyncComponent;
function noopUse() {
return function () { };
}
function noopRender() {
return null;
}
var DEFAULT_DISPLAY_NAME = 'Component';
var FILENAME_REGEX = /([^/]*)\.\w+$/;
function displayNameFromId(id) {
if (!id) {
return DEFAULT_DISPLAY_NAME;
}
var match = id.match(FILENAME_REGEX);
return match ? match[1] : DEFAULT_DISPLAY_NAME;
}
function defaultRenderError(error) {
if (error) {
throw error;
}
return null;
}
function Loader(_a) {
var defer = _a.defer, load = _a.load, props = _a.props;
var handleIntersection = react_1.useCallback(function (_a) {
var _b = _a.isIntersecting, isIntersecting = _b === void 0 ? true : _b;
if (isIntersecting) {
load();
}
}, [load]);
react_1.useEffect(function () {
if (defer == null || defer === async_1.DeferTiming.Mount) {
load();
}
else if (typeof defer === 'function' && defer(props)) {
load();
}
}, [defer, load, props]);
if (typeof defer === 'function') {
return null;
}
switch (defer) {
case async_1.DeferTiming.Idle:
return react_1.default.createElement(react_idle_1.OnIdle, { perform: load });
case async_1.DeferTiming.InViewport:
return (react_1.default.createElement(react_intersection_observer_1.IntersectionObserver, { threshold: 0, onIntersectionChange: handleIntersection }));
default:
return null;
}
}