Kalau satu komponen error waktu render dan gak ada yang menangkapnya, React membongkar seluruh tampilan app, dan layarnya jadi putih. Error boundary adalah komponen yang menangkap error dari komponen di dalamnya waktu render, lalu menampilkan tampilan cadangan (fallback UI) di tempat itu saja, sementara bagian app yang lain tetap jalan. Bayangin MCB, sakelar pengaman listrik per ruangan di rumah. Kalau ada korsleting di dapur, cuma MCB dapur yang turun dan lampu dapur mati, ruang tamu tetap terang. Tanpa MCB per ruangan, satu korsleting mematikan listrik seluruh rumah.
🧪 Tebakan cepat: di dalam
ErrorBoundaryada tombol denganonClick={() => { throw new Error("Gagal menyimpan") }}. Tombolnya diklik. Apakah fallback-nya muncul? Tebak dulu, jawabannya ada di bagian "Yang gak ditangkap".
Satu error, satu layar putih
function Profil({ user }) {
return <p>Halo, {user.nama}</p>
}
function App() {
const [user, setUser] = useState({ nama: "Budi" })
return (
<div>
<Profil user={user} />
<p>Saldo: Rp 250.000</p>
<button onClick={() => setUser(null)}>Rusakkan data</button>
</div>
)
}
Klik "Rusakkan data": Profil render lagi dengan user berisi null, dan user.nama melempar error. Di Chrome pesannya TypeError: Cannot read properties of null (reading 'nama'). Gak ada yang menangkapnya, jadi React membuang seluruh tampilan App, termasuk saldo yang gak salah apa-apa.
Membuat ErrorBoundary
Error boundary harus ditulis sebagai class component: komponen yang dibuat dengan class, bukan function. Sampai sekarang belum ada hook bawaan React untuk ini.
class ErrorBoundary extends React.Component {
state = { hasError: false }
static getDerivedStateFromError(error) {
return { hasError: true }
}
componentDidCatch(error, info) {
console.log("Tertangkap:", error.message)
}
render() {
if (this.state.hasError) {
return <div id="fallback">Terjadi error</div>
}
return this.props.children
}
}
function App() {
// ... user, setUser seperti tadi
return (
<div>
<ErrorBoundary>
<Profil user={user} />
</ErrorBoundary>
<p>Saldo: Rp 250.000</p>
</div>
)
}
class ErrorBoundary extends React.Componentmembuat class component. State-nya ada dithis.state, dan tampilannya dikembalikan methodrender().static getDerivedStateFromError(error)dipanggil React waktu komponen di dalamnya error saat render. Yang dikembalikan jadi state baru:{ hasError: true }.componentDidCatch(error, info)dipanggil sesudahnya. Ini tempat mencatat error, ke console atau ke layanan pemantau error.info.componentStackmemberi tahu komponen mana yang error.render()memilih:hasErrortrue? Tampilkan fallback<div id="fallback">Terjadi error</div>. False? Tampilkanthis.props.children, yaitu apa pun yang dibungkusnya.
| Kejadian | hasError |
Yang tampil |
|---|---|---|
| Halaman dibuka | false |
Halo, Budi dan Saldo: Rp 250.000 |
| Klik "Rusakkan data" | true |
Terjadi error dan Saldo: Rp 250.000 |
Saldo di luar boundary tetap tampil. Sama dengan MCB: yang mati cuma ruangan yang korsleting.
Coba lagi: kembali ke tampilan normal
Fallback biasanya punya tombol "Coba lagi" yang menjalankan this.setState({ hasError: false }), supaya boundary menampilkan anaknya lagi. Perbaiki penyebabnya dulu: kalau user masih null, Profil langsung error lagi dan fallback-nya muncul lagi.
Pola tampilannya sama dengan conditional rendering biasa: satu state boolean memilih satu dari dua tampilan. Di latihan kedua, pola itu ditulis dengan useState:
function App() {
const [hasError, setHasError] = useState(false)
return (
<div>
{hasError ? (
<div id="error-ui">Oops! Terjadi kesalahan.</div>
) : (
<div id="normal-ui">Semua berjalan normal</div>
)}
<button id="trigger" onClick={() => setHasError(true)}>Trigger Error</button>
<button id="reset" onClick={() => setHasError(false)}>Reset</button>
</div>
)
}
- Saat
hasErrortrue, cuma#error-uiyang ada.#normal-uibenar-benar hilang, bukan tampil berdampingan. - Tombol Reset mengembalikan
hasErrorkefalse, jadi#normal-uikembali dan#error-uihilang.
Yang gak ditangkap
Error boundary cuma menangkap error yang terjadi waktu React sedang merender: di dalam function komponen, di constructor, dan di method lifecycle seperti componentDidMount. Yang lolos:
- Error di event handler seperti
onClick. Waktu handler jalan, React gak sedang render, jadi boundary gak tahu apa-apa. Fallback-nya gak muncul, tampilan tetap seperti semula, dan console menulisUncaught Error: Gagal menyimpan. Itu jawaban tebakannya. - Error di kode async: di dalam
setTimeout, promise, ataufetch. - Error di dalam boundary itu sendiri. Yang menangkap boundary lain di atasnya.
Untuk event handler dan async, tangkap sendiri dengan try/catch, lalu simpan pesannya di state supaya bisa ditampilkan:
function handleSimpan() {
try {
simpanData()
} catch (error) {
setPesan("Gagal menyimpan: " + error.message)
}
}
⚠️ Jebakan yang sering ketemu
- Mengira boundary menangkap error
onClickataufetch. Gak. Pakaitry/catchdan state. - "Coba lagi" tanpa memperbaiki penyebabnya. Data yang sama bikin error yang sama, dan fallback-nya muncul lagi.
- Cuma satu boundary di paling atas. Satu widget error, seluruh halaman diganti fallback. Pasang juga per bagian: per halaman, per widget yang berat.
- Menulis boundary sebagai function component.
getDerivedStateFromErrordancomponentDidCatchcuma ada di class. Kalau gak mau menulis class, libraryreact-error-boundarymenyediakan komponenErrorBoundarysiap pakai.
TL;DR: Error waktu render yang gak tertangkap membuang seluruh tampilan app. Error boundary adalah class component dengan static getDerivedStateFromError (mengubah state jadi { hasError: true }) dan componentDidCatch(error, info) (mencatat error-nya). Selama hasError true, render() menampilkan fallback seperti <div id="fallback">Terjadi error</div>, dan bagian di luar boundary tetap tampil. Reset dengan setState({ hasError: false }) setelah penyebabnya diperbaiki. Boundary gak menangkap error di event handler, kode async, atau dirinya sendiri: tangkap itu dengan try/catch dan simpan pesannya di state.