Panduan komprehensif untuk memahami dan menerapkan JavaScript Error Boundary di React untuk penanganan error yang tangguh dan degradasi UI yang mulus.
JavaScript Error Boundary: Panduan Implementasi Penanganan Error React
Dalam dunia pengembangan React, error yang tidak terduga dapat menyebabkan pengalaman pengguna yang frustasi dan ketidakstabilan aplikasi. Strategi penanganan error yang terdefinisi dengan baik sangat penting untuk membangun aplikasi yang tangguh dan andal. Error Boundary dari React menyediakan mekanisme yang kuat untuk menangani error yang terjadi di dalam pohon komponen Anda dengan mulus, mencegah seluruh aplikasi mogok dan memungkinkan Anda menampilkan UI pengganti (fallback UI).
Apa itu Error Boundary?
Error Boundary adalah komponen React yang menangkap error JavaScript di mana saja dalam pohon komponen turunannya (child component tree), mencatat error tersebut, dan menampilkan UI pengganti alih-alih pohon komponen yang mogok. Error Boundary menangkap error selama proses rendering, dalam metode siklus hidup (lifecycle methods), dan dalam konstruktor dari seluruh pohon di bawahnya.
Anggaplah Error Boundary sebagai blok try...catch
untuk komponen React. Sama seperti blok try...catch
yang memungkinkan Anda menangani eksepsi dalam kode JavaScript sinkron, Error Boundary memungkinkan Anda menangani error yang terjadi selama proses rendering komponen React Anda.
Catatan Penting: Error Boundary tidak menangkap error untuk:
- Penangan event (event handler) (pelajari lebih lanjut di bagian berikutnya)
- Kode asinkron (misalnya, callback
setTimeout
ataurequestAnimationFrame
) - Rendering sisi server (Server-side rendering)
- Error yang dilempar di dalam Error Boundary itu sendiri (bukan di dalam turunannya)
Mengapa Menggunakan Error Boundary?
Menggunakan Error Boundary menawarkan beberapa keuntungan signifikan:
- Peningkatan Pengalaman Pengguna: Alih-alih menampilkan layar putih kosong atau pesan error yang membingungkan, Anda dapat menampilkan UI pengganti yang ramah pengguna, memberi tahu pengguna bahwa terjadi kesalahan dan berpotensi menawarkan cara untuk pulih (misalnya, memuat ulang halaman atau menavigasi ke bagian lain).
- Stabilitas Aplikasi: Error Boundary mencegah error di satu bagian aplikasi Anda agar tidak membuat seluruh aplikasi mogok. Ini sangat penting untuk aplikasi kompleks dengan banyak komponen yang saling terhubung.
- Penanganan Error Terpusat: Error Boundary menyediakan lokasi terpusat untuk mencatat error dan melacak akar penyebab masalah. Ini menyederhanakan proses debugging dan pemeliharaan.
- Degradasi yang Mulus (Graceful Degradation): Anda dapat menempatkan Error Boundary secara strategis di sekitar berbagai bagian aplikasi Anda untuk memastikan bahwa meskipun beberapa komponen gagal, sisa aplikasi tetap berfungsi. Ini memungkinkan degradasi yang mulus saat menghadapi error.
Mengimplementasikan Error Boundary di React
Untuk membuat Error Boundary, Anda perlu mendefinisikan sebuah komponen kelas yang mengimplementasikan salah satu (atau keduanya) dari metode siklus hidup berikut:
static getDerivedStateFromError(error)
: Metode siklus hidup ini dipanggil setelah sebuah error dilemparkan oleh komponen turunan. Metode ini menerima error yang dilemparkan sebagai argumen dan harus mengembalikan nilai untuk memperbarui state komponen guna menandakan bahwa telah terjadi error (misalnya, mengatur flaghasError
menjaditrue
).componentDidCatch(error, info)
: Metode siklus hidup ini dipanggil setelah sebuah error dilemparkan oleh komponen turunan. Metode ini menerima error yang dilemparkan sebagai argumen, bersama dengan objekinfo
yang berisi informasi tentang komponen mana yang melempar error. Anda dapat menggunakan metode ini untuk mencatat error ke layanan seperti Sentry atau Bugsnag.
Berikut adalah contoh dasar komponen Error Boundary:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Perbarui state agar render berikutnya akan menampilkan UI pengganti.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, info) {
// Contoh "componentStack":
// in ComponentThatThrows (created by App)
// in MyErrorBoundary (created by App)
// in div (created by App)
// in App
console.error("Caught an error:", error, info);
this.setState({
errorInfo: info.componentStack
});
// Anda juga bisa mencatat error ke layanan pelaporan error
//logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// Anda bisa merender UI pengganti kustom apa pun
return (
<div>
<h2>Terjadi kesalahan.</h2>
<p>Error: {this.state.error ? this.state.error.message : "Terjadi error yang tidak diketahui."}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.errorInfo && this.state.errorInfo}
</details>
</div>
);
}
return this.props.children;
}
}
Untuk menggunakan Error Boundary, cukup bungkus pohon komponen yang ingin Anda lindungi:
<ErrorBoundary>
<MyComponentThatMightThrow/>
</ErrorBoundary>
Contoh Praktis Penggunaan Error Boundary
Mari kita jelajahi beberapa skenario praktis di mana Error Boundary bisa sangat berguna:
1. Menangani Error API
Saat mengambil data dari API, error dapat terjadi karena masalah jaringan, masalah server, atau data yang tidak valid. Anda dapat membungkus komponen yang mengambil dan menampilkan data dengan Error Boundary untuk menangani error ini dengan mulus.
function UserProfile() {
const [user, setUser] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
async function fetchData() {
try {
const response = await fetch('/api/user');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
// Error akan ditangkap oleh ErrorBoundary
throw error;
} finally {
setIsLoading(false);
}
}
fetchData();
}, []);
if (isLoading) {
return <p>Memuat profil pengguna...</p>;
}
if (!user) {
return <p>Tidak ada data pengguna yang tersedia.</p>;
}
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
);
}
function App() {
return (
<ErrorBoundary>
<UserProfile />
</ErrorBoundary>
);
}
Dalam contoh ini, jika panggilan API gagal atau mengembalikan error, Error Boundary akan menangkap error tersebut dan menampilkan UI pengganti (didefinisikan dalam metode render
Error Boundary). Ini mencegah seluruh aplikasi mogok dan memberikan pesan yang lebih informatif kepada pengguna. Anda bisa memperluas UI pengganti untuk menyediakan opsi untuk mencoba kembali permintaan.
2. Menangani Error dari Pustaka Pihak Ketiga
Saat menggunakan pustaka pihak ketiga, ada kemungkinan mereka akan melempar error yang tidak terduga. Membungkus komponen yang menggunakan pustaka ini dengan Error Boundary dapat membantu Anda menangani error ini dengan mulus.
Pertimbangkan sebuah pustaka grafik hipotetis yang sesekali melempar error karena inkonsistensi data atau masalah lain. Anda bisa membungkus komponen grafik seperti ini:
function MyChartComponent() {
try {
// Render grafik menggunakan pustaka pihak ketiga
return <Chart data={data} />;
} catch (error) {
// Blok catch ini tidak akan efektif untuk error siklus hidup komponen React
// Ini terutama untuk error sinkron di dalam fungsi spesifik ini.
console.error("Error rendering chart:", error);
// Pertimbangkan untuk melempar error agar ditangkap oleh ErrorBoundary
throw error; // Melempar ulang error
}
}
function App() {
return (
<ErrorBoundary>
<MyChartComponent />
</ErrorBoundary>
);
}
Jika komponen Chart
melempar error, Error Boundary akan menangkapnya dan menampilkan UI pengganti. Perhatikan bahwa try/catch di dalam MyChartComponent hanya akan menangkap error di dalam fungsi sinkron, bukan siklus hidup komponen. Oleh karena itu, ErrorBoundary sangat penting di sini.
3. Menangani Error Rendering
Error dapat terjadi selama proses rendering karena data yang tidak valid, tipe prop yang salah, atau masalah lain. Error Boundary dapat menangkap error ini dan mencegah aplikasi mogok.
function DisplayName({ name }) {
if (typeof name !== 'string') {
throw new Error('Nama harus berupa string');
}
return <h2>Halo, {name}!</h2>;
}
function App() {
return (
<ErrorBoundary>
<DisplayName name={123} /> <!-- Tipe prop salah -->
</ErrorBoundary>
);
}
Dalam contoh ini, komponen DisplayName
mengharapkan prop name
berupa string. Jika sebuah angka yang diberikan, sebuah error akan dilemparkan, dan Error Boundary akan menangkapnya dan menampilkan UI pengganti.
Error Boundary dan Penangan Event (Event Handler)
Seperti yang disebutkan sebelumnya, Error Boundary tidak menangkap error yang terjadi di dalam penangan event. Ini karena penangan event biasanya bersifat asinkron, dan Error Boundary hanya menangkap error yang terjadi selama rendering, dalam metode siklus hidup, dan dalam konstruktor.
Untuk menangani error di penangan event, Anda perlu menggunakan blok try...catch
tradisional di dalam fungsi penangan event.
function MyComponent() {
const handleClick = () => {
try {
// Beberapa kode yang mungkin melempar error
throw new Error('Terjadi error di dalam penangan event');
} catch (error) {
console.error('Menangkap error di penangan event:', error);
// Tangani error (misalnya, tampilkan pesan error kepada pengguna)
}
};
return <button onClick={handleClick}>Klik Saya</button>;
}
Penanganan Error Global
Meskipun Error Boundary sangat baik untuk menangani error di dalam pohon komponen React, mereka tidak mencakup semua skenario error yang mungkin terjadi. Misalnya, mereka tidak menangkap error yang terjadi di luar komponen React, seperti error di dalam event listener global atau error dalam kode yang berjalan sebelum React diinisialisasi.
Untuk menangani jenis error ini, Anda dapat menggunakan penangan event window.onerror
.
window.onerror = function(message, source, lineno, colno, error) {
console.error('Penangan error global:', message, source, lineno, colno, error);
// Catat error ke layanan seperti Sentry atau Bugsnag
// Tampilkan pesan error global kepada pengguna (opsional)
return true; // Mencegah perilaku penanganan error default
};
Penangan event window.onerror
dipanggil setiap kali terjadi error JavaScript yang tidak tertangkap. Anda dapat menggunakannya untuk mencatat error, menampilkan pesan error global kepada pengguna, atau mengambil tindakan lain untuk menangani error tersebut.
Penting: Mengembalikan true
dari penangan event window.onerror
mencegah browser menampilkan pesan error default. Namun, perhatikan pengalaman pengguna; jika Anda menekan pesan default, pastikan Anda menyediakan alternatif yang jelas dan informatif.
Praktik Terbaik Menggunakan Error Boundary
Berikut adalah beberapa praktik terbaik yang perlu diingat saat menggunakan Error Boundary:
- Tempatkan Error Boundary secara strategis: Bungkus bagian-bagian berbeda dari aplikasi Anda dengan Error Boundary untuk mengisolasi error dan mencegahnya merambat. Pertimbangkan untuk membungkus seluruh rute atau bagian utama UI Anda.
- Sediakan UI pengganti yang informatif: UI pengganti harus memberi tahu pengguna bahwa telah terjadi error dan berpotensi menawarkan cara untuk pulih. Hindari menampilkan pesan error generik seperti "Terjadi kesalahan."
- Catat error: Gunakan metode siklus hidup
componentDidCatch
untuk mencatat error ke layanan seperti Sentry atau Bugsnag. Ini akan membantu Anda melacak akar penyebab masalah dan meningkatkan stabilitas aplikasi Anda. - Jangan gunakan Error Boundary untuk error yang diharapkan: Error Boundary dirancang untuk menangani error yang tidak terduga. Untuk error yang diharapkan (misalnya, error validasi, error API), gunakan mekanisme penanganan error yang lebih spesifik, seperti blok
try...catch
atau komponen penanganan error kustom. - Pertimbangkan beberapa level Error Boundary: Anda dapat menyarangkan Error Boundary untuk menyediakan level penanganan error yang berbeda. Misalnya, Anda mungkin memiliki Error Boundary global yang menangkap setiap error yang tidak tertangani dan menampilkan pesan error generik, dan Error Boundary yang lebih spesifik yang menangkap error di komponen tertentu dan menampilkan pesan error yang lebih detail.
- Jangan lupakan rendering sisi server: Jika Anda menggunakan rendering sisi server, Anda juga perlu menangani error di server. Error Boundary berfungsi di server, tetapi Anda mungkin perlu menggunakan mekanisme penanganan error tambahan untuk menangkap error yang terjadi selama render awal.
Teknik Error Boundary Tingkat Lanjut
1. Menggunakan Render Prop
Alih-alih merender UI pengganti yang statis, Anda dapat menggunakan render prop untuk memberikan fleksibilitas lebih dalam cara menangani error. Sebuah render prop adalah prop fungsi yang digunakan komponen untuk merender sesuatu.
class ErrorBoundary extends React.Component {
// ... (sama seperti sebelumnya)
render() {
if (this.state.hasError) {
// Gunakan render prop untuk merender UI pengganti
return this.props.fallbackRender(this.state.error, this.state.errorInfo);
}
return this.props.children;
}
}
function App() {
return (
<ErrorBoundary fallbackRender={(error, errorInfo) => (
<div>
<h2>Terjadi kesalahan!</h2>
<p>Error: {error.message}</p>
<details style={{ whiteSpace: 'pre-wrap' }}>
{errorInfo.componentStack}
</details>
</div>
)}>
<MyComponentThatMightThrow/>
</ErrorBoundary>
);
}
Ini memungkinkan Anda untuk menyesuaikan UI pengganti pada basis per-Error Boundary. Prop fallbackRender
menerima info error dan error sebagai argumen, memungkinkan Anda untuk menampilkan pesan error yang lebih spesifik atau mengambil tindakan lain berdasarkan error tersebut.
2. Error Boundary sebagai Higher-Order Component (HOC)
Anda dapat membuat higher-order component (HOC) yang membungkus komponen lain dengan Error Boundary. Ini bisa berguna untuk menerapkan Error Boundary ke beberapa komponen tanpa harus mengulangi kode yang sama.
function withErrorBoundary(WrappedComponent) {
return class WithErrorBoundary extends React.Component {
render() {
return (
<ErrorBoundary>
<WrappedComponent {...this.props} />
</ErrorBoundary>
);
}
};
}
// Penggunaan:
const MyComponentWithErrorHandling = withErrorBoundary(MyComponentThatMightThrow);
Fungsi withErrorBoundary
mengambil komponen sebagai argumen dan mengembalikan komponen baru yang membungkus komponen asli dengan Error Boundary. Ini memungkinkan Anda untuk dengan mudah menambahkan penanganan error ke komponen mana pun di aplikasi Anda.
Menguji Error Boundary
Penting untuk menguji Error Boundary Anda untuk memastikan bahwa mereka bekerja dengan benar. Anda dapat menggunakan pustaka pengujian seperti Jest dan React Testing Library untuk menguji Error Boundary Anda.
Berikut adalah contoh cara menguji Error Boundary menggunakan React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';
function ComponentThatThrows() {
throw new Error('Komponen ini melempar error');
}
test('merender UI pengganti saat sebuah error dilemparkan', () => {
render(
<ErrorBoundary>
<ComponentThatThrows />
</ErrorBoundary>
);
expect(screen.getByText('Terjadi kesalahan.')).toBeInTheDocument();
});
Tes ini merender komponen ComponentThatThrows
, yang melempar error. Tes kemudian menegaskan bahwa UI pengganti yang dirender oleh Error Boundary ditampilkan.
Error Boundary dan Server Components (React 18+)
Dengan diperkenalkannya Server Components di React 18 dan versi lebih baru, Error Boundary terus memainkan peran penting dalam penanganan error. Server Components dieksekusi di server dan hanya mengirimkan output yang sudah dirender ke klien. Meskipun prinsip intinya tetap sama, ada beberapa nuansa yang perlu dipertimbangkan:
- Pencatatan Error Sisi Server: Pastikan Anda mencatat error yang terjadi di dalam Server Components di server. Ini mungkin melibatkan penggunaan kerangka kerja pencatatan sisi server atau mengirim error ke layanan pelacakan error.
- UI Pengganti Sisi Klien: Meskipun Server Components dirender di server, Anda tetap perlu menyediakan UI pengganti sisi klien jika terjadi error. Ini memastikan bahwa pengguna memiliki pengalaman yang konsisten, bahkan jika server gagal merender komponen.
- Streaming SSR: Saat menggunakan streaming Server-Side Rendering (SSR), error dapat terjadi selama proses streaming. Error Boundary dapat membantu Anda menangani error ini dengan mulus dengan merender UI pengganti untuk stream yang terpengaruh.
Penanganan error di Server Components adalah area yang terus berkembang, jadi penting untuk tetap mengikuti praktik dan rekomendasi terbaru.
Kesalahan Umum yang Harus Dihindari
- Terlalu Bergantung pada Error Boundary: Jangan gunakan Error Boundary sebagai pengganti penanganan error yang tepat di komponen Anda. Selalu berusaha menulis kode yang tangguh dan andal yang menangani error dengan mulus.
- Mengabaikan Error: Pastikan Anda mencatat error yang ditangkap oleh Error Boundary sehingga Anda dapat melacak akar penyebab masalah. Jangan hanya menampilkan UI pengganti dan mengabaikan error tersebut.
- Menggunakan Error Boundary untuk Error Validasi: Error Boundary bukanlah alat yang tepat untuk menangani error validasi. Gunakan teknik validasi yang lebih spesifik.
- Tidak Menguji Error Boundary: Uji Error Boundary Anda untuk memastikan mereka bekerja dengan benar.
Kesimpulan
Error Boundary adalah alat yang ampuh untuk membangun aplikasi React yang tangguh dan andal. Dengan memahami cara mengimplementasikan dan menggunakan Error Boundary secara efektif, Anda dapat meningkatkan pengalaman pengguna, mencegah aplikasi mogok, dan menyederhanakan proses debugging. Ingatlah untuk menempatkan Error Boundary secara strategis, menyediakan UI pengganti yang informatif, mencatat error, dan menguji Error Boundary Anda secara menyeluruh.
Dengan mengikuti panduan dan praktik terbaik yang diuraikan dalam panduan ini, Anda dapat memastikan bahwa aplikasi React Anda tahan terhadap error dan memberikan pengalaman positif bagi pengguna Anda.