This commit is contained in:
Sabda Yagra 2025-12-08 17:10:46 +07:00
commit 8aa45b1351
3 changed files with 158 additions and 0 deletions

View File

@ -19,6 +19,7 @@ import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
import DirectionProvider from "@/providers/direction-provider";
import AuthProvider from "@/providers/auth.provider";
import ChunkLoadErrorHandler from "@/components/chunk-load-error-handler";
export const metadata: Metadata = {
title: "Media Hub | POLRI",
@ -96,6 +97,7 @@ export default async function RootLayout({
/>
</head>
<body className={`${inter.className} dashcode-app`}>
<ChunkLoadErrorHandler />
<NextIntlClientProvider messages={messages} locale={locale}>
<AuthProvider>
<ThemeProvider attribute="class" defaultTheme="light">

View File

@ -0,0 +1,82 @@
"use client";
import { useEffect } from "react";
/**
* Component to handle ChunkLoadError automatically
* When a chunk fails to load (usually after deployment),
* this component will force refresh the page once
*/
export default function ChunkLoadErrorHandler() {
useEffect(() => {
// Key to track if we've already refreshed for this error
const REFRESH_KEY = "chunk-load-error-refreshed";
// Handle unhandled promise rejections (for dynamic imports)
const handlePromiseRejection = (event: PromiseRejectionEvent) => {
const error = event.reason;
// Check if it's a ChunkLoadError
if (
error?.name === "ChunkLoadError" ||
error?.message?.includes("Loading chunk") ||
error?.message?.includes("ChunkLoadError") ||
error?.message?.includes("Failed to fetch dynamically imported module") ||
error?.message?.includes("failed to load resource")
) {
handleChunkLoadError();
}
};
// Handle regular errors
const handleError = (event: ErrorEvent) => {
const error = event.error;
// Check if it's a ChunkLoadError
if (
error?.name === "ChunkLoadError" ||
event.message?.includes("Loading chunk") ||
event.message?.includes("ChunkLoadError") ||
event.message?.includes("Failed to fetch dynamically imported module")
) {
handleChunkLoadError();
}
};
const handleChunkLoadError = () => {
// Check if we've already refreshed
const hasRefreshed = sessionStorage.getItem(REFRESH_KEY);
if (!hasRefreshed) {
console.log("ChunkLoadError detected. Refreshing page...");
// Mark that we've refreshed
sessionStorage.setItem(REFRESH_KEY, "true");
// Force reload the page (bypassing cache)
window.location.reload();
} else {
console.error("ChunkLoadError persists after refresh. There might be a deeper issue.");
// Clear the flag after 30 seconds in case user navigates away and comes back
setTimeout(() => {
sessionStorage.removeItem(REFRESH_KEY);
}, 30000);
}
};
// Add event listeners
window.addEventListener("error", handleError);
window.addEventListener("unhandledrejection", handlePromiseRejection);
// Cleanup event listeners
return () => {
window.removeEventListener("error", handleError);
window.removeEventListener("unhandledrejection", handlePromiseRejection);
};
}, []);
// This component doesn't render anything
return null;
}

View File

@ -0,0 +1,74 @@
# Chunk Load Error Handler
## Problem
ChunkLoadError terjadi ketika aplikasi Next.js mencoba memuat chunk JavaScript yang tidak lagi tersedia, biasanya terjadi karena:
- Deployment baru sementara user masih membuka halaman lama
- File chunk sudah berubah atau dihapus
- Network issues
- Cache issues
Error ini menyebabkan tampilan web bermasalah dan user tidak bisa menggunakan fitur tertentu.
## Solution
Implementasi automatic force refresh yang akan:
1. Mendeteksi ChunkLoadError secara otomatis
2. Melakukan force refresh **hanya sekali** untuk mencegah infinite reload loop
3. Menggunakan `sessionStorage` untuk tracking refresh state
## Implementation
### 1. Component: `chunk-load-error-handler.tsx`
Component client-side yang mendengarkan error events:
- `window.error` - untuk menangkap error reguler
- `window.unhandledrejection` - untuk menangkap promise rejections dari dynamic imports
Component ini akan:
- Mendeteksi berbagai variasi ChunkLoadError
- Check apakah sudah pernah refresh (via sessionStorage)
- Jika belum, lakukan `window.location.reload()` untuk force refresh
- Jika sudah refresh tapi masih error, tidak akan refresh lagi (mencegah infinite loop)
### 2. Integration di Root Layout
Component ditambahkan di root layout (`app/[locale]/layout.tsx`) agar berfungsi di seluruh aplikasi.
## How It Works
```
User opens app → Deployment happens → User navigates → ChunkLoadError occurs
Handler detects error
Check sessionStorage
No refresh yet? → Yes → Set flag & Reload
Already refreshed? → Show error in console
```
## Benefits
✅ User experience lebih baik - no more broken page
✅ Automatic recovery dari ChunkLoadError
✅ Aman - hanya refresh sekali, tidak ada infinite loop
✅ Bekerja di background tanpa mengganggu user
✅ Works untuk semua tipe lazy-loaded components
## Testing
Untuk test handler ini:
1. Build aplikasi: `npm run build`
2. Start production: `npm run start`
3. Buka aplikasi di browser
4. Build ulang aplikasi (tanpa menutup browser)
5. Navigate ke halaman yang menggunakan dynamic imports
6. Handler akan otomatis refresh jika terjadi ChunkLoadError
## Monitoring
Check browser console untuk melihat log:
- `"ChunkLoadError detected. Refreshing page..."` - saat refresh pertama kali
- `"ChunkLoadError persists after refresh..."` - jika masih error setelah refresh
## Notes
- SessionStorage akan di-clear otomatis setelah 30 detik jika error persists
- Handler tidak render apapun (return null)
- Zero impact pada performance
- Compatible dengan semua browser modern