fix: auto-reload on stale chunk errors after deploy

When a new build changes chunk hashes, users with cached index.html
get "Failed to fetch dynamically imported module" errors. Now:
- lazyWithRetry() wraps all lazy imports with auto-reload on failure
- ErrorBoundary catches chunk errors and auto-reloads once
- 30s guard in sessionStorage prevents reload loops
This commit is contained in:
c0mrade
2026-03-26 20:55:29 +03:00
parent 94b9b9eb94
commit c697ddb224
2 changed files with 148 additions and 97 deletions

View File

@@ -11,6 +11,18 @@ interface ErrorBoundaryState {
error: Error | null;
}
function isChunkLoadError(error: Error): boolean {
const msg = error.message || '';
return (
msg.includes('dynamically imported module') ||
msg.includes('Importing a module script failed') ||
msg.includes('Failed to fetch dynamically imported module') ||
msg.includes('Loading chunk') ||
msg.includes('ChunkLoadError') ||
error.name === 'ChunkLoadError'
);
}
export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
@@ -23,6 +35,19 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('[ErrorBoundary]', error, errorInfo);
// Auto-reload on chunk load failures (stale deploy)
if (isChunkLoadError(error)) {
const reloadKey = 'chunk_reload_ts';
const lastReload = Number(sessionStorage.getItem(reloadKey) || '0');
const now = Date.now();
// Prevent reload loop — only auto-reload once per 30 seconds
if (now - lastReload > 30_000) {
sessionStorage.setItem(reloadKey, String(now));
window.location.reload();
return;
}
}
}
handleReset = () => {
@@ -36,6 +61,7 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
}
const { level = 'page' } = this.props;
const isChunk = this.state.error ? isChunkLoadError(this.state.error) : false;
if (level === 'app') {
return (
@@ -78,13 +104,15 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
<div className="mb-4 text-4xl"></div>
<h1 className="mb-2 text-xl font-bold text-dark-50">Something went wrong</h1>
<p className="mb-6 text-sm text-dark-400">
{this.state.error?.message || 'An unexpected error occurred'}
{isChunk
? 'App was updated. Reloading...'
: this.state.error?.message || 'An unexpected error occurred'}
</p>
<button
onClick={this.handleReset}
onClick={() => window.location.reload()}
className="rounded-xl bg-accent-500 px-6 py-3 font-medium text-white transition-colors hover:bg-accent-600"
>
Try again
{isChunk ? 'Reload' : 'Try again'}
</button>
</div>
</div>