import { lazy } from 'react'; | |
const lazyWithRetry = (componentImport) => | |
lazy(async () => { | |
const pageHasAlreadyBeenForceRefreshed = JSON.parse( | |
window.localStorage.getItem( | |
'page-has-been-force-refreshed' | |
) || 'false' | |
); | |
try { | |
const component = await componentImport(); | |
window.localStorage.setItem( | |
'page-has-been-force-refreshed', | |
'false' | |
); | |
return component; | |
} catch (error) { | |
if (!pageHasAlreadyBeenForceRefreshed) { | |
// Assuming that the user is not on the latest version of the application. | |
// Let's refresh the page immediately. | |
window.localStorage.setItem( | |
'page-has-been-force-refreshed', | |
'true' | |
); | |
return window.location.reload(); | |
} | |
// The page has already been reloaded | |
// Assuming that user is already using the latest version of the application. | |
// Let's let the application crash and raise the error. | |
throw error; | |
} | |
}); |
hola me gustaría que me ayudaras con este error ya que estoy tratando de dar una solución al chunkloaderror pero en angular
@raphael-leger any chance you can provide the typescript version of this script?
import { ComponentType, lazy as originalLazy } from 'react';
type ImportComponent = () => Promise<{ default: ComponentType }>;
export const lazy = (importComponent: ImportComponent) =>
originalLazy((async () => {
const isPageHasBeenForceRefreshed = JSON.parse(
localStorage.getItem('page-has-been-force-refreshed') || 'false',
);
try {
const component = await importComponent();
localStorage.setItem('page-has-been-force-refreshed', 'false');
return component;
} catch (error) {
if (!isPageHasBeenForceRefreshed) {
localStorage.setItem('page-has-been-force-refreshed', 'true');
return location.reload();
}
throw error;
}
}) as ImportComponent);
@victorlitvinenko awesome thanks
AFAIK, the reloading approach only really works/makes sense in non-chromium browsers? From my own testing, failed module loading is persistent across manual reloads and browser restarts in chromium-derivates (Chrome, Edge, ...), and I was unable to get it to work again before manually clearing the cache.
So that's why we ended up going for the cache busting approach, as mentioned in Alon Mizrahi's article, when encountering this situation. I made a tiny library based on his approach (which parses the error message to get the module path), but improved on the cross-browser situation by finding another approach that works in Firefox (and probably all other Ecmascript engines as well). If you npm install @fatso83/retry-dynamic-import
you can then use reactLazyWithRetry( () => import('./foo') )
or opt for wrapping the vanilla dynamicImportWithRetry
in some other way.
@lisethgira Google Translate to the rescue 😄
I am not 100% sure how you do lazy loading of components in Angular, but you should essentially be able to just replace your import('./foo')
with dynamicImportWithRetry( () => import('./foo')
. I had a look at the Angular docs, which mention using loadChildren
, so it would look something like this:
import {dynamicImportWithRetry} from '@fatso83/retry-dynamic-import';
//... other code
const routes: Routes = [
{
path: 'items',
loadChildren: () => dynamicImportWithRetry(() => import('./items/items.module')).then(m => m.ItemsModule)
}
];
I was hitting a number of issues with the above code snippets - including:
- Incompatible types
- Lack of code completion on the resulting component
- Using multiple lazyRetry functions on the same page would result in infinite loops caused by a successful component resetting the boolean for an unsuccessful component
Here's my result, incase it's useful to anyone else:
const lazyRetry = <T extends ComponentType<never>>(
importComponent: () => Promise<{ default: T }>
): LazyExoticComponent<T> =>
originalLazy<T>(async () => {
const stringifiedFunction = importComponent.toString();
const componentsRefreshed: string[] =
JSON.parse(sessionStorage.getItem('reloaded-components')) || [];
let refreshedComponents: Set<string>;
if (Array.isArray(componentsRefreshed)) {
refreshedComponents = new Set(componentsRefreshed);
} else {
throw Error('Unexpected value from data store');
}
const hasComponentRefreshed = refreshedComponents.has(stringifiedFunction);
try {
const component = await importComponent();
refreshedComponents.delete(stringifiedFunction);
sessionStorage.setItem(
'reloaded-components',
JSON.stringify(Array.from(refreshedComponents))
);
return component;
} catch (error) {
if (!hasComponentRefreshed) {
refreshedComponents.add(stringifiedFunction);
sessionStorage.setItem(
'reloaded-components',
JSON.stringify(Array.from(refreshedComponents))
);
location.reload();
} else {
throw error;
}
}
});
export { lazyRetry };
@chrisbruford see my above comment regarding inapplicability in chromium browsers; does the forced reloading work for you? For me, the failed resolution seems persistent, and only cache busting the module will actually fix it (clearing cache also works).
From reading on the Chromium issue tracker, this is by design btw.
@fatso83 we have some separate cache issues so I can't confirm just yet. Once they're resolved I'll be able to see if this turns up again and let you know.
@raphael-leger any chance you can provide the typescript version of this script?