UNPKG

@suspensive/react

Version:

Suspensive interfaces for react

185 lines (182 loc) 6.61 kB
'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