133 lines
5.0 KiB
Markdown
133 lines
5.0 KiB
Markdown
# 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
|
|
↓
|
|
Validate it's actual ChunkLoadError
|
|
(not API error or normal network error)
|
|
↓
|
|
Check sessionStorage
|
|
↓
|
|
No refresh yet? → Yes → Set flag & Reload (100ms delay)
|
|
↓
|
|
Already refreshed? → Show error in console
|
|
```
|
|
|
|
### Error Detection Logic
|
|
Handler hanya mendeteksi ChunkLoadError yang spesifik:
|
|
- Error name adalah "ChunkLoadError"
|
|
- Error message mengandung "Loading chunk" atau "ChunkLoadError"
|
|
- Error terkait `_next/static` atau `_next` (Next.js chunks)
|
|
- **Mengecualikan** error API, HTTP requests, atau service calls
|
|
|
|
Ini mencegah false positive dari Promise rejection biasa.
|
|
|
|
## 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
|
|
✅ Tidak konflik dengan Promise rejection biasa (API calls, etc)
|
|
✅ Prevention untuk multiple simultaneous refresh
|
|
✅ Time-based protection (tidak refresh jika < 5 detik dari refresh terakhir)
|
|
|
|
## 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
|
|
|
|
## Promise Handling & Best Practices
|
|
|
|
### Mencegah Konflik dengan Promise
|
|
Handler telah dioptimasi untuk tidak konflik dengan Promise yang sedang berjalan:
|
|
|
|
1. **Spesifik Error Detection**: Hanya mendeteksi ChunkLoadError yang benar-benar terkait Next.js chunks (`_next/static`, `_next/`), tidak menangkap API errors atau network errors biasa.
|
|
|
|
2. **Component Cleanup**: Komponen yang menggunakan async initialization (seperti `spit-convert-form.tsx`) harus menggunakan cleanup pattern:
|
|
```typescript
|
|
useEffect(() => {
|
|
let isMounted = true;
|
|
|
|
const init = async () => {
|
|
if (isMounted) {
|
|
await initializeComponent();
|
|
}
|
|
};
|
|
|
|
init();
|
|
|
|
return () => {
|
|
isMounted = false;
|
|
};
|
|
}, []);
|
|
```
|
|
|
|
3. **AbortError Handling**: Catch block harus mengecualikan AbortError:
|
|
```typescript
|
|
catch (error: any) {
|
|
// Skip error alert for AbortError (navigation/refresh)
|
|
if (error?.name === 'AbortError' || error?.message?.includes('abort')) {
|
|
return;
|
|
}
|
|
// Handle other errors...
|
|
}
|
|
```
|
|
|
|
### Time-Based Protection
|
|
- Handler tidak akan refresh jika refresh terakhir < 5 detik yang lalu
|
|
- Flags akan di-clear otomatis setelah 5 menit untuk session baru
|
|
- Delay 100ms sebelum reload untuk mencegah immediate re-trigger
|
|
|
|
## Notes
|
|
- SessionStorage akan di-clear otomatis setelah 1 menit jika error persists
|
|
- Handler tidak render apapun (return null)
|
|
- Zero impact pada performance
|
|
- Compatible dengan semua browser modern
|
|
- Event listeners menggunakan capture phase untuk prioritas lebih tinggi
|
|
|