diff --git a/src/CONST.ts b/src/CONST.ts index c28e23ea61b5..60d2af4ecb71 100755 --- a/src/CONST.ts +++ b/src/CONST.ts @@ -5166,6 +5166,7 @@ const CONST = { SESSION_STORAGE_KEYS: { INITIAL_URL: 'INITIAL_URL', ACTIVE_WORKSPACE_ID: 'ACTIVE_WORKSPACE_ID', + RETRY_LAZY_REFRESHED: 'RETRY_LAZY_REFRESHED', }, RESERVATION_TYPE: { diff --git a/src/libs/Navigation/AppNavigator/index.tsx b/src/libs/Navigation/AppNavigator/index.tsx index f8b14781a5ec..787ede6c14f2 100644 --- a/src/libs/Navigation/AppNavigator/index.tsx +++ b/src/libs/Navigation/AppNavigator/index.tsx @@ -2,9 +2,10 @@ import React, {lazy, memo, Suspense, useContext, useEffect} from 'react'; import {NativeModules} from 'react-native'; import {InitialURLContext} from '@components/InitialURLContextProvider'; import Navigation from '@libs/Navigation/Navigation'; +import lazyRetry from '@src/utils/lazyRetry'; -const AuthScreens = lazy(() => import('./AuthScreens')); -const PublicScreens = lazy(() => import('./PublicScreens')); +const AuthScreens = lazy(() => lazyRetry(() => import('./AuthScreens'))); +const PublicScreens = lazy(() => lazyRetry(() => import('./PublicScreens'))); type AppNavigatorProps = { /** If we have an authToken this is true */ diff --git a/src/utils/lazyRetry.ts b/src/utils/lazyRetry.ts new file mode 100644 index 000000000000..35c6b444e612 --- /dev/null +++ b/src/utils/lazyRetry.ts @@ -0,0 +1,42 @@ +import type {ComponentType} from 'react'; +import CONST from '@src/CONST'; + +type Import = Promise<{default: T}>; +type ComponentImport = () => Import; + +/** + * Attempts to lazily import a React component with a retry mechanism on failure. + * If the initial import fails the function will refresh the page once and retry the import. + * If the import fails again after the refresh, the error is propagated. + * + * @param componentImport - A function that returns a promise resolving to a lazily imported React component. + * @returns A promise that resolves to the imported component or rejects with an error after a retry attempt. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const lazyRetry = function >(componentImport: ComponentImport): Import { + return new Promise((resolve, reject) => { + // Retrieve the retry status from sessionStorage, defaulting to 'false' if not set + const hasRefreshed = JSON.parse(sessionStorage.getItem(CONST.SESSION_STORAGE_KEYS.RETRY_LAZY_REFRESHED) ?? 'false') as boolean; + + componentImport() + .then((component) => { + // Reset the retry status to 'false' on successful import + sessionStorage.setItem(CONST.SESSION_STORAGE_KEYS.RETRY_LAZY_REFRESHED, 'false'); // success so reset the refresh + resolve(component); + }) + .catch((component: ComponentImport) => { + if (!hasRefreshed) { + console.error('Failed to lazily import a React component, refreshing the page in order to retry the operation.', component); + // Set the retry status to 'true' and refresh the page + sessionStorage.setItem(CONST.SESSION_STORAGE_KEYS.RETRY_LAZY_REFRESHED, 'true'); + window.location.reload(); // Refresh the page to retry the import + } else { + console.error('Failed to lazily import a React component after the retry operation!', component); + // If the import fails again reject with the error to trigger default error handling + reject(component); + } + }); + }); +}; + +export default lazyRetry;