Skip to content

Latest commit

 

History

History
412 lines (318 loc) · 10.7 KB

File metadata and controls

412 lines (318 loc) · 10.7 KB

Error Boundary Implementation - React Frontend Error Handling

Overview

An Error Boundary is a React component that catches JavaScript errors anywhere in the component tree, logs those errors, and displays a user-friendly fallback UI instead of crashing the entire app.


Components Created

1. ErrorBoundary Component

File: frontend/src/components/ErrorBoundary.jsx

A class component that implements React Error Boundary lifecycle methods:

Key Methods:

getDerivedStateFromError(error) - Static Method

  • Called during render phase when an error is caught
  • Returns new state to trigger fallback UI render
  • Runs before componentDidCatch
  • Purpose: Update UI state to show error message
static getDerivedStateFromError(error) {
  return {
    hasError: true,
    errorMessage: error?.message || 'An unexpected error occurred',
  };
}

componentDidCatch(error, errorInfo) - Instance Method

  • Called during commit phase (after render fails)
  • Receives error object and errorInfo with componentStack
  • Can perform side effects (logging, error reporting)
  • Purpose: Log errors and send to external services
componentDidCatch(error, errorInfo) {
  console.error('Error caught by ErrorBoundary:', error);
  console.error('Error Info:', errorInfo);
  
  // Store error details for display
  this.setState({
    errorDetails: {
      message: error?.message,
      stack: error?.stack,
      componentStack: errorInfo?.componentStack,
      timestamp: new Date().toISOString(),
    },
  });

  // Optionally log to backend
  this.logErrorToBackend(error, errorInfo);
}

State:

{
  hasError: false,           // Whether an error has been caught
  errorMessage: '',          // User-friendly error message
  errorDetails: null,        // Full error stack and component info
}

Features:

  1. User-Friendly Fallback UI

    • Shows error emoji icon (⚠️)
    • Friendly title: "Oops! Something Went Wrong"
    • Context-appropriate message
    • Helpful instruction: "Try reloading the page"
    • Prominent "Reload" button
  2. Development Error Details

    • In development mode: Shows expandable details with full error stack
    • Includes component stack trace from React
    • Includes timestamp and error message
    • Only visible in NODE_ENV === 'development'
  3. Error Logging

    • Console logging (always)
    • Backend logging (POST to /api/errors)
    • Includes user agent, URL, timestamp
    • Non-blocking (wrapped in try-catch)
  4. Reload Functionality

    • Button triggers window.location.reload()
    • Allows user to recover app state
    • Simple and reliable recovery method

Integration in App.jsx

File: frontend/src/App.jsx

ErrorBoundary wraps the entire app at the highest level:

export default function App() {
  return (
    <ErrorBoundary>
      <AuthProvider>
        <LoadingProvider>
          <AppContent />
        </LoadingProvider>
      </AuthProvider>
    </ErrorBoundary>
  );
}

Why at the top level?

  • Catches errors in ALL child components
  • Catches errors in providers (less common but possible)
  • Prevents entire app from going blank
  • Provides consistent error handling across app

Styling

The fallback UI includes:

  • Centered container with gradient background
  • White error box with shadow and rounded corners
  • Responsive design with proper padding and max-width
  • Visual hierarchy with icon, title, message
  • Dev-friendly details section (hidden in production)
  • Green action button matching app theme

Error Handling Scope

✅ What Error Boundaries Catch:

  • Component render errors
  • Lifecycle method errors (except async ones)
  • Constructor errors
  • Errors in any child component

❌ What Error Boundaries Do NOT Catch:

  • Event handler errors (use try-catch in handlers)
  • Async code errors (setTimeout, promises)
  • Server-side rendering errors
  • Errors in the boundary itself

Example - Event Handler (NOT caught):

// This error won't be caught by boundary
// Need try-catch inside handler
<button onClick={() => throw new Error('Test')}>Click</button>

// Correct approach:
<button onClick={() => {
  try {
    // risky operation
  } catch (e) {
    // handle error
  }
}}>Click</button>

Acceptance Criteria - All Met ✅

A component error shows a friendly message instead of a blank screen

  • Error boundary catches component render errors
  • Displays user-friendly fallback UI
  • Shows "Oops! Something Went Wrong" message
  • App remains interactive (not blank white screen)

The fallback UI includes a Reload button that refreshes the page

  • Button styled as primary action (green)
  • Calls window.location.reload() on click
  • Allows user to recover app without hard refresh
  • Page reloads and app reinitializes

Error details are logged to the browser console

  • componentDidCatch logs to console
  • Includes error object with stack trace
  • Includes errorInfo with component stack
  • Always logged regardless of mode

The error boundary does not catch errors in event handlers (expected behavior)

  • Error boundaries only catch render errors
  • Event handler errors require try-catch
  • This is standard React behavior (not a limitation)
  • Can be written in eventhandler code if needed

The app recovers normally after reloading

  • Page reload reinitializes all components
  • Auth state rehydrated from localStorage if stored
  • No permanent damage from error
  • App returns to normal functioning state

Backend Error Logging (Optional)

The component attempts to log errors to /api/errors:

logErrorToBackend = (error, errorInfo) => {
  try {
    fetch('/api/errors', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        message: error?.message || 'Unknown error',
        stack: error?.stack,
        componentStack: errorInfo?.componentStack,
        url: window.location.href,
        userAgent: navigator.userAgent,
        timestamp: new Date().toISOString(),
      }),
    }).catch(e => console.error('Failed to log error to backend:', e));
  } catch (err) {
    console.error('Error logging to backend:', err);
  }
};

Features:

  • Non-blocking (doesn't affect user experience)
  • Safe error handling (try-catch wrapped)
  • Includes useful debugging info
  • Backend can ignore if endpoint not implemented
  • Can be implemented later without changing component

Example Backend Endpoint (future enhancement):

// POST /api/errors
// Could store errors in database for analysis
// Could send alerts to developers
// Could track error patterns

Testing the Error Boundary

Manual Testing - Throw Error:

Option 1: Add test error to a component:

function TestErrorComponent() {
  if (true) throw new Error('Test error');
  return <div>Content</div>;
}

// Then use: <TestErrorComponent /> somewhere

Option 2: Use browser console:

// In DevTools console while app is running:
throw new Error('Manual test error');

// This will trigger the boundary!

Expected Behavior:

  1. Component renders normally
  2. Error is thrown during render
  3. ErrorBoundary catches it
  4. Fallback UI appears with error message
  5. Console shows error logs
  6. User can click "Reload Page" button
  7. Page reloads, app recovers

Development Testing:

When NODE_ENV === 'development':

  • Error details section is visible
  • Expandable <details> element
  • Shows full error stack and component stack
  • Helps with debugging

Production Behavior:

When NODE_ENV === 'production':

  • Error details section is hidden
  • Shows only user-friendly message
  • Errors still logged to console
  • Backend error logging still works

Browser Compatibility

Requires:

  • React 16.8+ (error boundaries added in 16.0)
  • Modern browser (all modern browsers support)

No additional dependencies - uses standard React APIs


Files Modified

  1. frontend/src/components/ErrorBoundary.jsx (NEW)

    • 183 lines
    • React class component
    • Error boundary implementation
    • Fallback UI and styling
  2. frontend/src/App.jsx (MODIFIED)

    • Added import: import ErrorBoundary from './components/ErrorBoundary'
    • Wrapped return statement with <ErrorBoundary> component
    • No changes to existing logic or providers

Implementation Details

State Management

state = {
  hasError: false,           // Triggered by getDerivedStateFromError
  errorMessage: '',          // From error.message
  errorDetails: null,        // Set in componentDidCatch
}

Render Logic

if (this.state.hasError) {
  return <ErrorFallback UI />;  // Show error screen
}
return this.props.children;     // Normal render

Error Info Captured

The errorInfo parameter includes:

  • componentStack: Call stack of components that threw
  • Trace of React component hierarchy
  • Useful for debugging which component failed

Future Enhancements

Possible improvements without changing current implementation:

  1. Backend Integration

    • Create /api/errors endpoint
    • Store errors in database
    • Create error dashboard
    • Set up error notifications
  2. Error Recovery Strategies

    • Auto-reload after timeout
    • Retry with exponential backoff
    • Fallback to previous state
  3. Error Tracking Service

    • Integrate with Sentry or similar
    • Track error patterns
    • Alert on critical errors
  4. User Communication

    • Error code for support reference
    • Support contact info in fallback UI
    • Automatic error report generation
  5. Development Tools

    • Error overlay in dev
    • Component error isolation
    • Error history tracking

Common Issues & Solutions

Issue: Error boundary not catching error

Cause: Error might be in event handler or async code Solution: Use try-catch in event handlers, wrap promises

Issue: Error details not showing

Cause: Production build hides details Solution: Check NODE_ENV or use development build

Issue: Reload button doesn't work

Cause: Unusual page setup or routing issues Solution: Check browser console for errors during reload

Issue: App shows blank screen anyway

Cause: Error in ErrorBoundary itself or outer error Solution: Check console; boundary errors not caught by itself


Summary

✅ Error boundary catches component render errors
✅ Shows friendly fallback UI instead of blank screen
✅ Includes reload button for user recovery
✅ Logs errors to console for debugging
✅ Optional backend error logging
✅ Development-friendly with detailed error info
✅ Production-friendly with user-facing messages
✅ Zero breaking changes to existing code
✅ All acceptance criteria met