Skip to content

Instantly share code, notes, and snippets.

@ivanbtrujillo
Created November 30, 2023 11:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ivanbtrujillo/005db52e4a8ef8fd1dc8f2db3b442d45 to your computer and use it in GitHub Desktop.
Save ivanbtrujillo/005db52e4a8ef8fd1dc8f2db3b442d45 to your computer and use it in GitHub Desktop.
react-lazy-with-reload.ts
/**
* This is a wrap for React.Lazy based on Christoph Nakazawa solution (https://gist.github.com/cpojer/c50a742ec943d95c3c72a41ac1c06f03) for the issue that happens when using React.Lazy and we deploy a new version of a module, changing the hash (automatically managed by bundlers).
*
* THE PROBLEM:
* When our React application is compiled for production, packaging tools (Vite in this case) often add hashes to the filename to handle caching.
* These hashes change whenever the content of the file changes. This ensures that users get the most recent version of the file, since a change in the hash will force the browser to download the new file instead of serving a cached version.
*
* If a user has your application open and you make a new deployment, the file names of the dynamically loaded modules may change (due to the new hashes).
* And then, if the user navigates to a part of your application that requires loading a new module, their browser will attempt to load the module using the old file name (which is in the previously loaded code), which will result in an error as that file no longer exists on the server.
*
* THE SOLUTION - WORKAROUND:
*
* A wrap for React.Lazy that reloads the page if the module is not found.
* If before 10 seconds, the error appears again, we assue it is not an error related to this issue and we throw the error up, letting
* React Error Boundaries (https://react.dev/reference/react/useTransition#displaying-an-error-to-users-with-error-boundary) manage it.
*
* If you want to manage the error inside the react-lazy wrapper, check Christoph solution.
**/
import type { ComponentType } from 'react';
import * as React from 'react';
export const lazy = (factory: () => Promise<{ default: ComponentType<any> }>) => {
return React.lazy(() => factory().catch(importErrorHandler));
};
export function importErrorHandler(err:string): { default: ComponentType<any> } {
// Get the last reload time from local storage and the current time
const timeStr = sessionStorage.getItem('last-reload');
const time = timeStr ? Number(timeStr) : null;
const now = Date.now();
// If the last reload time is more than 10 seconds ago, reload the page
const isReloading = !time || time + 10_000 < now;
if (isReloading) {
console.log('New version for this module found. Reloading ...')
sessionStorage.setItem('last-reload', String(now));
window.location.reload();
return { default: () => null }
}
// We let ErrorBoundary handle the error
throw new Error(err);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment