Error Boundaries
Error Boundaries
Section titled “Error Boundaries”Simple Words First 🟡
Section titled “Simple Words First 🟡”Analogy: 👉 Imagine a circuit breaker in your house. When one room has an electrical fault, the breaker trips for that room — but the rest of the house still has power. An error boundary is the same: if one component crashes, the error boundary catches it and shows a fallback, without breaking the whole app.
Error Boundary Catch Flow
Section titled “Error Boundary Catch Flow”flowchart TB Child["Child Component\\nthrows an error"] --> Check["Error Boundary\\ncatches the error"] Check --> Decide{"getDerivedStateFromError\\nhasError = true"} Decide -->|"No"| Pass["Render passes through\\nnormal update"] Decide -->|"Yes"| Fallback["Render fallback UI\\ncomponentDidCatch logs\\nthe error info"]
style Child fill:#dc2626,color:#fff style Check fill:#4f46e5,color:#fff style Decide fill:#d97706,color:#fff style Fallback fill:#059669,color:#fff style Pass fill:#10b981,color:#fffWhy Error Boundaries Must Be Class Components
Section titled “Why Error Boundaries Must Be Class Components”React hooks don’t have the lifecycle methods needed to catch errors. Error boundaries must be class components because they need getDerivedStateFromError() and componentDidCatch().
import { Component } from 'react';
class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { hasError: false, error: null }; }
// 1️⃣ Update state so the next render shows fallback UI static getDerivedStateFromError(error) { return { hasError: true, error }; }
// 2️⃣ Log error info (send to error reporting service) componentDidCatch(error, info) { console.error('Caught:', error); console.error('Component stack:', info.componentStack); // Send to Sentry / LogRocket / your logging service logErrorToService(error, info); }
render() { if (this.state.hasError) { // You can render ANY custom fallback UI return this.props.fallback || ( <div className="error-boundary"> <h2>⚠️ Something went wrong</h2> <p>{this.state.error?.message}</p> <button onClick={() => this.setState({ hasError: false, error: null })}> Try again </button> </div> ); }
return this.props.children; }}
// Usagefunction App() { return ( <ErrorBoundary fallback={<p>❌ User section crashed</p>}> <UserProfile userId={123} /> </ErrorBoundary> );}Where to Place Error Boundaries
Section titled “Where to Place Error Boundaries”// Wrap the whole app (one fallback for everything)<ErrorBoundary> <App /></ErrorBoundary>
// Wrap individual sections (granular recovery)<section> <ErrorBoundary fallback={<ErrorWidget />}> <UserList /> </ErrorBoundary> <ErrorBoundary fallback={<ErrorWidget />}> <ChatWidget /> </ErrorBoundary></section>
// Wrap a single component<ErrorBoundary> <HeavyDataTable data={data} /></ErrorBoundary>What Error Boundaries DON’T Catch
Section titled “What Error Boundaries DON’T Catch”// ❌ Event handlers — use try/catch insteadfunction Button() { const handleClick = () => { try { // risky operation } catch (err) { console.error(err); } }; return <button onClick={handleClick}>Click</button>;}
// ❌ Async code (setTimeout, requestAnimationFrame)setTimeout(() => { try { // risky operation } catch (err) { console.error(err); }}, 1000);
// ❌ Server-side rendering// ❌ Errors thrown in the error boundary itselfIn Simple Words 🧠
Section titled “In Simple Words 🧠”- Error boundaries catch render errors in child components and show a fallback UI
- They must be class components — hooks can’t do this (yet)
- Place them at granular levels so one crash doesn’t take down the whole app
- They don’t catch event handler errors, async code, or SSR errors — use
try/catchfor those - The
componentDidCatchmethod is perfect for logging errors to monitoring services