@suspensive/react
Version:
Suspensive interfaces for react
185 lines (182 loc) • 6.61 kB
JavaScript
'use client';
import { noop } from "./utils/noop.mjs";
import { _objectSpread2 } from "./_virtual/_@oxc-project_runtime@0.130.0/helpers/objectSpread2.mjs";
import { _objectWithoutProperties } from "./_virtual/_@oxc-project_runtime@0.130.0/helpers/objectWithoutProperties.mjs";
import { lazy as lazy$1 } from "react";
//#region src/lazy.ts
const _excluded = [
"retry",
"retryDelay",
"storage",
"reload"
];
/**
* Creates a lazy function with custom default options
*
* The default `lazy` export is equivalent to `createLazy({})`.
*
* @experimental This is experimental feature.
*
* @description
* The created lazy function will execute individual callbacks first, then default callbacks.
* For onSuccess: individual onSuccess → default onSuccess
* For onError: individual onError → default onError
*
* @example
* ```tsx
* import { createLazy } from '@suspensive/react'
*
* // Create a lazy factory with custom default options
* const lazy = createLazy({
* onSuccess: () => console.log('Component loaded successfully'),
* onError: ({ error }) => console.error('Component loading failed:', error)
* })
*
* // Use the factory to create lazy components
* const Component1 = lazy(() => import('./Component1'))
* const Component2 = lazy(() => import('./Component2'))
*
* // Individual options are executed after default options
* const Component3 = lazy(() => import('./Component3'), {
* onError: ({ error }) => {
* console.error('Individual error handling:', error)
* // This callback runs after the default onError callback
* }
* })
* ```
*/
const createLazy = (defaultOptions) => (load, options) => {
const composedOnSuccess = ({ load }) => {
var _options$onSuccess, _defaultOptions$onSuc;
options === null || options === void 0 || (_options$onSuccess = options.onSuccess) === null || _options$onSuccess === void 0 || _options$onSuccess.call(options, { load });
(_defaultOptions$onSuc = defaultOptions.onSuccess) === null || _defaultOptions$onSuc === void 0 || _defaultOptions$onSuc.call(defaultOptions, { load });
};
const composedOnError = ({ error, load }) => {
var _options$onError, _defaultOptions$onErr;
options === null || options === void 0 || (_options$onError = options.onError) === null || _options$onError === void 0 || _options$onError.call(options, {
error,
load
});
(_defaultOptions$onErr = defaultOptions.onError) === null || _defaultOptions$onErr === void 0 || _defaultOptions$onErr.call(defaultOptions, {
error,
load
});
};
const loadNoReturn = () => load().then(noop);
return Object.assign(lazy$1(() => load().then((loaded) => {
composedOnSuccess({ load: loadNoReturn });
return loaded;
}, (error) => {
composedOnError({
error,
load: loadNoReturn
});
throw error;
})), { load: loadNoReturn });
};
/**
* A wrapper around React.lazy that provides error handling and success callbacks
*
* This is equivalent to `createLazy({})` - a lazy function with no default options.
*
* @experimental This is experimental feature.
*
* @example
* ```tsx
* import { lazy, Suspense } from '@suspensive/react'
*
* // Basic usage
* const Component = lazy(() => import('./Component'))
*
* // With error handling and success callbacks
* const ReloadComponent = lazy(() => import('./ReloadComponent'), {
* onError: ({ error }) => console.error('Loading failed:', error),
* onSuccess: () => console.log('Component loaded successfully')
* })
*
* // Preloading component
* function PreloadExample() {
* const handlePreload = () => {
* Component.load() // Preload the component
* }
*
* return (
* <div>
* <button onClick={handlePreload}>Preload Component</button>
* <Suspense fallback={<div>Loading...</div>}>
* <Component />
* </Suspense>
* </div>
* )
* }
*
* // Using createLazy for factory pattern
* const lazy = createLazy({
* onError: ({ error }) => console.error('Default error handling:', error)
* })
* const CustomComponent = lazy(() => import('./CustomComponent'))
* ```
*
* @returns A lazy component with additional `load` method for preloading
* @property {() => Promise<void>} load - Preloads the component without rendering it. Useful for prefetching components in the background.
*/
const lazy = createLazy({});
/**
* Options for reloading page if the component fails to load.
*
* @experimental This is experimental feature.
*
* @example
* ```tsx
* import { createLazy, reloadOnError } from '@suspensive/react'
*
* const lazy = createLazy(reloadOnError({ retry: 1, retryDelay: 1000 }))
* const Component = lazy(() => import('./Component'))
* ```
*/
const reloadOnError = (_ref) => {
let { retry = 1, retryDelay = 0, storage, reload } = _ref, options = _objectWithoutProperties(_ref, _excluded);
const reloadStorage = (() => {
if (storage) return storage;
if (typeof window !== "undefined" && "sessionStorage" in window) return window.sessionStorage;
throw new Error("[@suspensive/react] No storage provided and no sessionStorage in window");
})();
const reloadFunction = (() => {
if (reload) return reload;
if (typeof window !== "undefined" && "location" in window) return () => window.location.reload();
throw new Error("[@suspensive/react] No reload function provided and no location in window");
})();
return _objectSpread2(_objectSpread2({}, options), {}, {
onSuccess: ({ load }) => {
var _options$onSuccess2;
(_options$onSuccess2 = options.onSuccess) === null || _options$onSuccess2 === void 0 || _options$onSuccess2.call(options, { load });
reloadStorage.removeItem(load.toString());
},
onError: ({ error, load }) => {
var _options$onError2;
(_options$onError2 = options.onError) === null || _options$onError2 === void 0 || _options$onError2.call(options, {
error,
load
});
const storageKey = load.toString();
let currentRetryCount = 0;
if (typeof retry === "number") {
const storedValue = reloadStorage.getItem(storageKey);
if (storedValue) {
const reloadCount = parseInt(storedValue, 10);
if (Number.isNaN(reloadCount)) reloadStorage.removeItem(storageKey);
currentRetryCount = reloadCount;
}
}
if (!(retry === true || typeof retry === "number" && currentRetryCount < retry)) return;
if (typeof retry === "number") reloadStorage.setItem(storageKey, String(currentRetryCount + 1));
const delayValue = typeof retryDelay === "function" ? retryDelay(currentRetryCount) : retryDelay;
const timeoutId = setTimeout(() => {
reloadFunction(timeoutId);
}, delayValue);
}
});
};
//#endregion
export { createLazy, lazy, reloadOnError };
//# sourceMappingURL=lazy.mjs.map