-
-
Save alireza-ahmadi/5280d6d069f2398be6ce61d07d5f9c26 to your computer and use it in GitHub Desktop.
This utility function retryDynamicImport enhances React’s lazy loading mechanism by adding retry logic with a versioned query parameter. It retries importing a component multiple times in case of failure, which can be useful for bypassing browser cache or dealing with intermittent network issues. It can be used as a drop-in replacement for React…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Usage: | |
// Replace React.lazy(() => import('x')); | |
// with retryDynamicImport(() => import('x')); | |
import { ComponentType, lazy } from 'react'; | |
const MAX_RETRY_COUNT = 15; | |
const RETRY_DELAY_MS = 500; | |
// Regex to extract the module URL from the import statement | |
const uriOrRelativePathRegex = /import\(["']([^)]+)['"]\)/; | |
// Function to extract the component URL from the dynamic import | |
const getRouteComponentUrl = (originalImport: () => Promise<any>): string | null => { | |
try { | |
const fnString = originalImport.toString(); | |
return fnString.match(uriOrRelativePathRegex)?.[1] || null; | |
} catch (e) { | |
console.error('Error extracting component URL:', e); | |
return null; | |
} | |
}; | |
// Function to create a retry import function with versioned query parameter | |
const getRetryImportFunction = ( | |
originalImport: () => Promise<any>, | |
retryCount: number | |
): (() => Promise<any>) => { | |
const importUrl = getRouteComponentUrl(originalImport); | |
if (!importUrl || retryCount === 0) { | |
return originalImport; | |
} | |
// Add or update the version query parameter in the import URL | |
const importUrlWithVersionQuery = importUrl.includes('?') | |
? `${importUrl}&v=${retryCount}-${Math.random().toString(36).substring(2)}` | |
: `${importUrl}?v=${retryCount}-${Math.random().toString(36).substring(2)}`; | |
return () => import(/* @vite-ignore */ importUrlWithVersionQuery); | |
}; | |
// Main function to wrap the dynamic import with retry logic | |
export function retryDynamicImport<T extends ComponentType<any>>( | |
importFunction: () => Promise<{ default: T }> | |
): React.LazyExoticComponent<T> { | |
let retryCount = 0; | |
const loadComponent = (): Promise<{ default: T }> => | |
new Promise((resolve, reject) => { | |
(function tryLoadComponent() { | |
const retryImport = getRetryImportFunction(importFunction, retryCount); | |
retryImport() | |
.then((module) => { | |
if (retryCount > 0) { | |
console.log( | |
`Component loaded successfully after ${retryCount} ${retryCount === 1 ? 'retry' : 'retries'}.` | |
); | |
} | |
resolve(module); | |
}) | |
.catch((error) => { | |
retryCount += 1; | |
if (retryCount <= MAX_RETRY_COUNT) { | |
console.warn(`Retry attempt ${retryCount} failed, retrying...`); | |
setTimeout(() => { | |
tryLoadComponent(); | |
}, retryCount * RETRY_DELAY_MS); | |
} else { | |
console.error('Failed to load component after maximum retries. Reloading the page...'); | |
reject(error); | |
window.location.reload(); | |
} | |
}); | |
})(); | |
}); | |
return lazy(() => loadComponent()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment