Pelajari cara menerapkan strategi penanganan kesalahan React yang tangguh menggunakan Pohon Batas Kesalahan untuk degradasi yang anggun dan pengalaman pengguna yang lebih baik. Temukan praktik terbaik, teknik canggih, dan contoh dunia nyata.
Pohon Batas Kesalahan React: Penanganan Kesalahan Hierarkis untuk Aplikasi yang Tangguh
Arsitektur berbasis komponen React mendorong penggunaan kembali dan pemeliharaan, tetapi juga memperkenalkan potensi penyebaran kesalahan yang dapat mengganggu seluruh aplikasi. Kesalahan yang tidak ditangani dapat menyebabkan pengalaman yang tidak menyenangkan bagi pengguna, menampilkan pesan yang tidak jelas atau bahkan merusak aplikasi. Batas Kesalahan (Error Boundaries) menyediakan mekanisme untuk menangkap kesalahan JavaScript di mana saja dalam pohon komponen turunannya, mencatat kesalahan tersebut, dan menampilkan UI fallback alih-alih pohon komponen yang rusak. Pohon Batas Kesalahan yang dirancang dengan baik memungkinkan Anda untuk mengisolasi kegagalan dan memberikan pengalaman pengguna yang lebih baik dengan mendegradasi bagian-bagian tertentu dari aplikasi Anda secara anggun tanpa memengaruhi yang lain.
Memahami Batas Kesalahan React
Diperkenalkan di React 16, Batas Kesalahan adalah komponen React yang menangkap kesalahan JavaScript di mana saja dalam pohon komponen turunannya, mencatat kesalahan tersebut, dan menampilkan UI fallback alih-alih pohon komponen yang rusak. Batas Kesalahan menangkap kesalahan selama rendering, dalam metode siklus hidup, dan dalam konstruktor dari seluruh pohon di bawahnya. Pentingnya, mereka *tidak* menangkap kesalahan untuk:
- Penangan acara (event handler) (pelajari lebih lanjut di bawah)
- Kode asinkron (misalnya, callback
setTimeoutataurequestAnimationFrame) - Rendering sisi server
- Kesalahan yang dilemparkan di dalam batas kesalahan itu sendiri (bukan di turunannya)
Sebuah komponen kelas menjadi Batas Kesalahan jika mendefinisikan salah satu (atau keduanya) dari metode siklus hidup ini:
static getDerivedStateFromError(): Metode ini dipanggil setelah sebuah kesalahan dilemparkan oleh komponen turunan. Metode ini menerima kesalahan yang dilemparkan sebagai argumen dan harus mengembalikan nilai untuk memperbarui state.componentDidCatch(): Metode ini dipanggil setelah sebuah kesalahan dilemparkan oleh komponen turunan. Metode ini menerima dua argumen:error: Kesalahan yang dilemparkan.info: Sebuah objek yang berisi informasi tentang komponen mana yang melemparkan kesalahan.
Contoh Batas Kesalahan Sederhana
Berikut adalah komponen Batas Kesalahan dasar:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Perbarui state agar render berikutnya akan menampilkan UI fallback.
return { hasError: true };
}
componentDidCatch(error, info) {
// Anda juga dapat mencatat kesalahan ke layanan pelaporan kesalahan
console.error("Caught an error: ", error, info.componentStack);
//logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// Anda dapat merender UI fallback kustom apa pun
return <h1>Terjadi kesalahan.</h1>;
}
return this.props.children;
}
}
Penggunaan:
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
Kekuatan Pohon Batas Kesalahan
Meskipun satu Batas Kesalahan dapat melindungi seluruh aplikasi Anda, pendekatan yang lebih canggih melibatkan pembuatan *Pohon* Batas Kesalahan. Ini berarti menempatkan beberapa Batas Kesalahan secara strategis di berbagai tingkat hierarki komponen Anda. Ini memungkinkan Anda untuk:
- Mengisolasi Kegagalan: Kegagalan di satu bagian aplikasi tidak akan serta-merta merusak seluruh UI. Hanya bagian yang dibungkus oleh Batas Kesalahan tertentu yang akan menampilkan UI fallback.
- Menyediakan Fallback Sesuai Konteks: Bagian yang berbeda dari aplikasi Anda mungkin memerlukan UI fallback yang berbeda. Misalnya, komponen gambar yang gagal mungkin menampilkan gambar placeholder, sementara komponen pengambilan data yang gagal mungkin menampilkan tombol "Coba Lagi".
- Meningkatkan Pengalaman Pengguna: Dengan menempatkan Batas Kesalahan secara hati-hati, Anda dapat memastikan bahwa aplikasi Anda terdegradasi secara anggun, meminimalkan gangguan bagi pengguna.
Membangun Pohon Batas Kesalahan: Contoh Praktis
Mari kita pertimbangkan sebuah aplikasi web yang menampilkan profil pengguna. Profil tersebut terdiri dari beberapa bagian:
- Informasi Pengguna (nama, lokasi, bio)
- Foto Profil
- Umpan Aktivitas Terbaru
- Daftar Pengikut
Kita dapat membungkus setiap bagian ini dengan Batas Kesalahannya sendiri.
// ErrorBoundary.js (Komponen ErrorBoundary generik dari atas)
import ErrorBoundary from './ErrorBoundary';
function UserProfile() {
return (
<div>
<ErrorBoundary>
<UserInfo />
</ErrorBoundary>
<ErrorBoundary fallbackUI={<img src="/placeholder.png" alt="Placeholder"/>}>
<ProfilePicture />
</ErrorBoundary>
<ErrorBoundary fallbackUI={<p>Gagal memuat aktivitas. Silakan coba lagi nanti.</p>}>
<ActivityFeed />
</ErrorBoundary>
<ErrorBoundary fallbackUI={<p>Tidak dapat memuat pengikut.</p>}>
<FollowersList />
</ErrorBoundary>
</div>
);
}
Dalam contoh ini, jika komponen ProfilePicture gagal dimuat (misalnya, karena URL gambar yang rusak), hanya area foto profil yang akan menampilkan UI fallback (gambar placeholder). Sisa dari profil akan tetap berfungsi. Demikian pula, kegagalan pada komponen ActivityFeed hanya akan memengaruhi bagian itu, menampilkan pesan "Silakan coba lagi nanti".
Perhatikan penggunaan prop fallbackUI di beberapa komponen ErrorBoundary. Ini memungkinkan kita untuk menyesuaikan UI fallback untuk setiap bagian, memberikan pengalaman yang lebih sadar konteks dan ramah pengguna.
Teknik Batas Kesalahan Tingkat Lanjut
1. Menyesuaikan UI Fallback
UI fallback default (misalnya, pesan sederhana "Terjadi kesalahan") mungkin tidak cukup untuk semua skenario. Anda dapat menyesuaikan UI fallback untuk memberikan pesan yang lebih informatif, menawarkan tindakan alternatif, atau bahkan mencoba memulihkan dari kesalahan.
Seperti yang ditunjukkan pada contoh sebelumnya, Anda dapat menggunakan props untuk meneruskan UI fallback kustom ke komponen ErrorBoundary:
<ErrorBoundary fallbackUI={<CustomFallbackComponent />}>
<MyComponent />
</ErrorBoundary>
CustomFallbackComponent dapat menampilkan pesan kesalahan yang lebih spesifik, menyarankan langkah-langkah pemecahan masalah, atau menawarkan tombol "Coba Lagi".
2. Mencatat Kesalahan ke Layanan Eksternal
Meskipun Batas Kesalahan mencegah aplikasi mogok, sangat penting untuk mencatat kesalahan sehingga Anda dapat mengidentifikasi dan memperbaiki masalah yang mendasarinya. Metode componentDidCatch adalah tempat yang ideal untuk mencatat kesalahan ke layanan pelacakan kesalahan eksternal seperti Sentry, Bugsnag, atau Rollbar.
class ErrorBoundary extends React.Component {
// ...
componentDidCatch(error, info) {
// Catat kesalahan ke layanan pelaporan kesalahan
logErrorToMyService(error, info.componentStack);
}
// ...
}
Pastikan untuk mengonfigurasi layanan pelacakan kesalahan Anda untuk menangani kesalahan JavaScript dan memberi Anda informasi terperinci tentang kesalahan tersebut, termasuk jejak tumpukan komponen (component stack trace).
Contoh menggunakan Sentry:
import * as Sentry from "@sentry/react";
import { BrowserTracing } from "@sentry/tracing";
Sentry.init({
dsn: "YOUR_SENTRY_DSN",
integrations: [new BrowserTracing()],
// Atur tracesSampleRate ke 1.0 untuk menangkap 100%
// transaksi untuk pemantauan kinerja.
// Kami merekomendasikan untuk menyesuaikan nilai ini di produksi
tracesSampleRate: 1.0,
});
class ErrorBoundary extends React.Component {
// ...
componentDidCatch(error, info) {
Sentry.captureException(error, { extra: info });
}
// ...
}
3. Batas Kesalahan dan Penangan Acara
Seperti yang disebutkan sebelumnya, Batas Kesalahan *tidak* menangkap kesalahan di dalam penangan acara. Ini karena penangan acara dieksekusi secara asinkron, di luar siklus hidup rendering React. Untuk menangani kesalahan di penangan acara, Anda perlu menggunakan blok try...catch.
function MyComponent() {
const handleClick = () => {
try {
// Kode yang mungkin melemparkan kesalahan
throw new Error("Something went wrong in the event handler!");
} catch (error) {
console.error("Error in event handler:", error);
// Tampilkan pesan kesalahan kepada pengguna
alert("Terjadi kesalahan. Silakan coba lagi.");
}
};
return <button onClick={handleClick}>Klik Saya</button>;
}
4. Batas Kesalahan dan Operasi Asinkron
Demikian pula, Batas Kesalahan tidak menangkap kesalahan dalam operasi asinkron seperti setTimeout, setInterval, atau Promise. Anda perlu menggunakan blok try...catch di dalam operasi asinkron ini untuk menangani kesalahan.
Contoh dengan Promise:
function MyComponent() {
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Proses data
console.log(data);
} catch (error) {
console.error("Error fetching data:", error);
// Tampilkan pesan kesalahan kepada pengguna
alert("Gagal mengambil data. Silakan periksa koneksi Anda.");
}
};
fetchData();
}, []);
return <div>Memuat data...</div>;
}
5. Mencoba Ulang Operasi yang Gagal
Dalam beberapa kasus, mungkin saja untuk mencoba ulang operasi yang gagal secara otomatis. Misalnya, jika permintaan jaringan gagal karena masalah konektivitas sementara, Anda dapat mengimplementasikan mekanisme coba ulang dengan backoff eksponensial.
Anda dapat mengimplementasikan mekanisme coba ulang di dalam UI fallback atau di dalam komponen yang mengalami kesalahan. Pertimbangkan untuk menggunakan pustaka seperti axios-retry atau mengimplementasikan logika coba ulang Anda sendiri menggunakan setTimeout.
Contoh (coba ulang dasar):
function RetryComponent({ onRetry }) {
return <button onClick={onRetry}>Coba Lagi</button>;
}
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
console.error("Caught an error: ", error, info.componentStack);
}
handleRetry = () => {
this.setState({ hasError: false, error: null }, () => {
//Paksa render ulang komponen dengan memperbarui state
this.forceUpdate();
});
};
render() {
if (this.state.hasError) {
return (
<div>
<h1>Terjadi kesalahan.</h1>
<p>{this.state.error?.message}</p>
<RetryComponent onRetry={this.handleRetry} />
</div>
);
}
return this.props.children;
}
}
Praktik Terbaik Menggunakan Batas Kesalahan
- Bungkus Seluruh Rute: Untuk rute tingkat atas, pertimbangkan untuk membungkus seluruh rute dengan Batas Kesalahan untuk menangkap kesalahan tak terduga yang mungkin terjadi. Ini memberikan jaring pengaman dan mencegah seluruh aplikasi mogok.
- Bungkus Bagian Kritis: Identifikasi bagian paling kritis dari aplikasi Anda (misalnya, proses checkout di situs e-commerce) dan bungkus dengan Batas Kesalahan untuk memastikan bagian tersebut tahan terhadap kesalahan.
- Jangan Terlalu Sering Menggunakan Batas Kesalahan: Hindari membungkus setiap komponen dengan Batas Kesalahan. Ini dapat menambah overhead yang tidak perlu dan membuat kode Anda lebih sulit dibaca. Fokus pada pembungkusan komponen yang kemungkinan besar akan gagal atau yang sangat penting bagi pengalaman pengguna.
- Sediakan UI Fallback yang Informatif: UI fallback harus memberikan informasi yang jelas dan bermanfaat kepada pengguna tentang apa yang salah dan apa yang bisa mereka lakukan untuk menyelesaikan masalah. Hindari menampilkan pesan kesalahan generik yang tidak memberikan konteks apa pun.
- Catat Kesalahan Secara Menyeluruh: Pastikan untuk mencatat semua kesalahan yang ditangkap oleh Batas Kesalahan ke layanan pelacakan kesalahan eksternal. Ini akan membantu Anda mengidentifikasi dan memperbaiki masalah yang mendasarinya dengan cepat.
- Uji Batas Kesalahan Anda: Tulis pengujian unit dan pengujian integrasi untuk memastikan bahwa Batas Kesalahan Anda berfungsi dengan benar dan menangkap kesalahan yang diharapkan. Simulasikan kondisi kesalahan dan verifikasi bahwa UI fallback ditampilkan dengan benar.
- Pertimbangkan Penanganan Kesalahan Global: Meskipun Batas Kesalahan sangat bagus untuk menangani kesalahan di dalam komponen React, Anda juga harus mempertimbangkan untuk menerapkan penanganan kesalahan global untuk menangkap kesalahan yang terjadi di luar pohon React (misalnya, penolakan promise yang tidak tertangani).
Pertimbangan Global dan Sensitivitas Budaya
Saat merancang Pohon Batas Kesalahan untuk audiens global, penting untuk mempertimbangkan sensitivitas budaya dan lokalisasi:
- Lokalisasi: Pastikan UI fallback Anda dilokalkan dengan benar untuk berbagai bahasa dan wilayah. Gunakan pustaka lokalisasi seperti
i18nextataureact-intluntuk menerjemahkan pesan kesalahan dan teks lainnya. - Konteks Budaya: Perhatikan perbedaan budaya saat merancang UI fallback Anda. Hindari penggunaan gambar atau simbol yang mungkin menyinggung atau tidak pantas di budaya tertentu. Misalnya, isyarat tangan yang dianggap positif di satu budaya mungkin menyinggung di budaya lain.
- Zona Waktu: Jika pesan kesalahan Anda menyertakan stempel waktu atau informasi terkait waktu lainnya, pastikan untuk menampilkannya dalam zona waktu lokal pengguna.
- Mata Uang: Jika pesan kesalahan Anda melibatkan nilai moneter, tampilkan dalam mata uang lokal pengguna.
- Aksesibilitas: Pastikan UI fallback Anda dapat diakses oleh pengguna dengan disabilitas. Gunakan atribut ARIA yang sesuai dan ikuti pedoman aksesibilitas untuk membuat aplikasi Anda dapat digunakan oleh semua orang.
- Persetujuan Pelaporan Kesalahan (Opt-In): Bersikaplah transparan tentang pelaporan kesalahan. Beri pengguna opsi untuk memilih ikut (opt-in) atau tidak ikut (opt-out) dalam mengirimkan laporan kesalahan ke server Anda. Pastikan kepatuhan terhadap peraturan privasi seperti GDPR dan CCPA.
Contoh (Lokalisasi menggunakan `i18next`):
// i18n.js (konfigurasi i18next)
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import en from './locales/en/translation.json';
import fr from './locales/fr/translation.json';
i18n
.use(initReactI18next) // meneruskan i18n ke react-i18next
.init({
resources: {
en: { translation: en },
fr: { translation: fr },
},
lng: 'en', // bahasa default
fallbackLng: 'en',
interpolation: {
escapeValue: false, // react sudah aman dari xss
},
});
export default i18n;
// ErrorBoundary.js
import { useTranslation } from 'react-i18next';
function ErrorBoundary(props) {
const { t } = useTranslation();
// ...
render() {
if (this.state.hasError) {
return <h1>{t('error.somethingWentWrong')}</h1>;
}
return this.props.children;
}
}
Kesimpulan
Pohon Batas Kesalahan React adalah alat yang ampuh untuk membangun aplikasi yang tangguh dan andal. Dengan menempatkan Batas Kesalahan secara strategis di berbagai tingkat hierarki komponen Anda, Anda dapat mengisolasi kegagalan, menyediakan fallback sesuai konteks, dan meningkatkan pengalaman pengguna secara keseluruhan. Ingatlah untuk menangani kesalahan di penangan acara dan operasi asinkron menggunakan blok try...catch. Dengan mengikuti praktik terbaik dan mempertimbangkan faktor global serta budaya, Anda dapat membuat aplikasi yang andal dan ramah pengguna untuk audiens yang beragam.
Dengan menerapkan Pohon Batas Kesalahan yang dirancang dengan baik dan memperhatikan detail, Anda dapat secara signifikan meningkatkan keandalan dan pengalaman pengguna aplikasi React Anda, di mana pun lokasi pengguna Anda berada.