Error Boundaries

Di masa lalu, kesalahan JavaScript dalam komponen sering kali merusak state internal React dan menyebabkannya untuk meng-emit kesalahan yang samar dalam proses render berikutnya. Kesalahan tersebut selalu disebabkan oleh kesalahan sebelumnya pada kode aplikasi, tetapi React tidak menyediakan cara untuk menanganinya secara lugas dalam komponen, dan pada akhirnya tidak bisa dipulihkan.

Memperkenalkan Error Boundaries (Pembatas Kesalahan)

Kesalahan JavaScript dalam sebuah bagian antarmuka seharusnya tidak boleh menyebabkan kerusakan aplikasi secara keseluruhan. Untuk mengatasi masalah ini bagi pengguna React, React 16 memperkenalkan konsep baru yaitu “error boundary”.

Error boundaries merupakan komponen React yang menangkap kesalahan JavaScript di semua tempat di dalam pohon komponen, mencatat kesalahan tersebut, dan menampilkan antarmuka darurat (fallback) alih-alih menampilkan pohon komponen yang rusak. Error boundary menangkap kesalahan dalam proses render, dalam metode lifecycle, dan dalam konstruktor dari keseluruhan pohon di bawahnya.

Catatan

Error boundaries tidak menangkap kesalahan untuk:

  • Event handler (pelajari lebih lanjut)
  • Kode asinkronus (misalnya, setTimeout atau callback requestAnimationFrame)
  • Proses render sisi server
  • Kesalahan yang dilontarkan dalam komponen error boundary itu sendiri (bukan dalam anaknya)

Sebuah komponen kelas menjadi komponen error boundary jika komponen tersebut mendefinisikan salah satu (atau kedua) metode lifecycle static getDerivedStateFromError() atau componentDidCatch(). Gunakan static getDerivedStateFromError() untuk me-render antarmuka darurat saat kesalahan dilontarkan. Gunakan componentDidCatch() untuk mencatat informasi kesalahan.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {    // Perbarui state agar proses render berikutnya akan menampilkan antarmuka darurat.    return { hasError: true };  }
  componentDidCatch(error, errorInfo) {    // Anda juga bisa mencatat kesalahan ke layanan pencatat kesalahan    logErrorToMyService(error, errorInfo);  }
  render() {
    if (this.state.hasError) {      // Anda bisa menampilkan antarmuka darurat Anda di sini      return <h1>Something went wrong.</h1>;    }
    return this.props.children; 
  }
}

Setelahnya, Anda bisa menggunakannya seperti komponen biasa:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

Error boundaries bekerja seperti blok JavaScript catch {}, tetapi diperuntukkan bagi komponen. Hanya komponen kelas yang bisa menjadi komponen error boundaries. Dalam praktiknya, Anda hampir akan selalu mendeklarasikan sebuah komponen error boundary sekali lalu menggunakannya dalam aplikasi Anda.

Perhatikan bahwa error boundaries hanya menangkap kesalahan dalam komponen di bawah pohon. Komponen error boundary tidak dapat menangkap kesalahan dari dalam dirinya sendiri. Jika error boundary gagal me-render pesan kesalahan, kesalahan tersebut akan merambat ke komponen error boundary terdekat di atasnya. Ini mirip dengan cara kerja blok catch {} dalam JavaScript.

Demonstrasi Langsung

Kunjungi contoh tentang pendeklarasian dan penggunaan komponen error boundary dengan React 16.

Dimana Harus Meletakkan Error boundaries

Anda sendiri yang menentukan granularitas sebuah komponen error boundaries. Anda mungkin ingin membungkus komponen route tingkat teratas untuk menampilkan pesan “Ada masalah” kepada pengguna, seperti halnya yang dilakukan framework sisi server saat terjadi kerusakan. Anda mungkin juga bisa membungkus widget secara individual dengan sebuah error boundary untuk melindung kerusakan merambat ke seluruh aplikasi.

Perilaku Baru untuk Kesalahan yang Tidak Tertangkap

Perubahan ini memiliki dampak yang penting. Sejak React 16, kesalahan yang tidak ditangkap oleh error boundary apa pun akan menyebabkan proses pelepasan (mounting) keseluruhan pohon komponen React.

Kami berdebat cukup panjang hingga sampai pada keputusan ini. Namun menurut pengalaman kami, antarmuka yang rusak berdampak lebih buruk dibandingkan dengan benar-benar menghilangkannya. Misalnya, pada produk seperti Messenger, menyisakan tampilan antarmuka yang rusak kepada pengguna bisa menyebabkan seseorang mengirim pesan ke tujuan yang salah. Kasus yang mirip adalah dalam aplikasi pembayaran, dampak lebih buruk akan terjadi jika aplikasi menampilkan jumlah yang salah, dibandingkan dengan tidak menampilkannya sama sekali..

Perubahan ini berarti, seiring dengan migrasi Anda ke React 16, Anda lebih mungkin menemukan kerusakan yang ada dalam aplikasi, yang sebelumnya belum pernah ditemukan. Penambahan error boundaries dapat digunakan untuk menyediakan pengalaman yang lebih baik bagi pengguna Anda pada saat masalah terjadi.

Misalnya, Facebook Messenger membungkus konten bilah samping, panel info, catatan percakapan, serta input pesan ke dalam error boundaries yang berbeda. Jika beberapa komponen dalam antarmuka tersebut rusak, sisa antarmuka yang tampil tetap masih bersifat interaktif.

Kami juga menyarankan Anda untuk menggunakan layanan pelaporan kesalahan JavaScript (atau buatan Anda sendiri) agar Anda bisa mempelajari tentang eksepsi yang tidak tertangani dalam versi produksi, dan kemudian memperbaikinya.

Stack Trace Komponen

React 16 mencetak semua kesalahan yang terjadi dalam proses render ke konsol dalam tahap pengembangan, walau aplikasi tanpa sengaja mengenyampingkannya. Selain pesan kesalahan dan stack JavaScript, React juga menyediakan stack trace komponen. Kini Anda bisa melihat letak kegagalan dalam pohon komponen:

Kesalahan yang ditangkap komponen Error Boundary

Anda juga bisa melihat nama file dan nomor baris dalam stack trace komponen. Ini berfungsi secara default dalam proyek yang berasal dari Create React App:

Kesalahan yang ditangkap komponen Error Boundary beserta nomor baris

Jika Anda tidak menggunakan Create React App, Anda bisa menambahkan plugin ini secara manual dalam konfigurasi Babel Anda. Perhatikan bahwa ini ditujukan untuk tahap pengembangan dan harus dinonaktifkan dalam tahap produksi.

Catatan

Nama komponen yang ditampilkan dalam stack trace tergantung dari properti Function.name. Jika Anda mendukung browser dan perangkat versi lawas yang tidak mendukung fitur ini secara native (misalnya, IE 11), pertimbangkan untuk menyertakan polyfill Function.name dalam bundel aplikasi Anda, misalnya function.name-polyfill. Alternatifnya adalah Anda bisa menetapkan properti displayName secara eksplisit untuk semua komponen Anda.

Bagaimana dengan try/catch?

try / catch sangat baik, tetapi hanya bekerja untuk kode yang imperatif:

try {
  showButton();
} catch (error) {
  // ...
}

Sedangkan komponen React bersifat deklaratif dan menentukan apa yang harus di-render:

<Button />

Error boundaries mempertahankan sifat deklaratif React dan berperilaku seperti yang Anda harapkan. Misalnya, walau kesalahan terjadi dalam metode componentDidUpdate yang disebabkan oleh setState di suatu tempat dalam pohon, kesalahan tersebut masih akan terus merambat dengan benar ke error boundary terdekat.

Bagaimana dengan Event Handler?

Error boundaries tidak menangkap kesalahan dalam event handler.

React tidak membutuhkan error boundaries untuk memulihkan dari kesalahan dalam event handler. Tidak seperti metode render dan lifecycle, event handler tidak dijalankan selama proses render. Jadi jika kesalahan ini dilontarkan, React masih mengetahui apa yang harus ditampilkan dalam layar.

Jika Anda harus menangkap kesalahan dalam event handler, gunakan JavaScript statement try / catch biasa:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    try {      // Lakukan sesuatu yang bisa melontarkan kesalahan    } catch (error) {      this.setState({ error });    }  }

  render() {
    if (this.state.error) {      return <h1>Kesalahan ditangkap.</h1>    }    return <button onClick={this.handleClick}>Klik Saya</button>  }
}

Perhatikan bahwa contoh di atas mendemonstrasikan perilaku JavaScript biasa dan tidak menggunakan error boundaries.

Perubahan Nama Sejak React 15

React 15 menyertakan dukungan terbatas untuk error boundaries dengan nama metode yang berbeda: unstable_handleError. Metode ini tidak lagi berfungsi dan Anda harus mengubahnya menjadi componentDidCatch dalam kode, sejak versi rilis beta 16.

Untuk perubahan ini, kami menyediakan perintah codemod untuk memigrasikan kode Anda secara otomatis.