Error Handling
CredDAO uses React Error Boundaries for graceful error handling.
Basic Usage
ErrorBoundary Component
import { ErrorBoundary } from '@/components/ErrorBoundary'
<ErrorBoundary>
<RiskyComponent />
</ErrorBoundary>When an error occurs, displays a fallback UI with:
- Error message
- “Try Again” button to reset
Custom Fallback
<ErrorBoundary
fallback={<CustomErrorUI />}
>
<Component />
</ErrorBoundary>Specialized Boundaries
WalletErrorBoundary
For wallet-related errors:
import { WalletErrorBoundary } from '@/components/ErrorBoundary'
<WalletErrorBoundary>
<WalletDependentComponent />
</WalletErrorBoundary>Shows: “Wallet connection error. Please refresh and reconnect.”
ProposalErrorBoundary
For proposal rendering:
import { ProposalErrorBoundary } from '@/components/ErrorBoundary'
<ProposalErrorBoundary>
<ProposalCard proposal={proposal} />
</ProposalErrorBoundary>Shows: “Failed to load proposal. It may have been removed.”
VoteErrorBoundary
For voting components:
import { VoteErrorBoundary } from '@/components/ErrorBoundary'
<VoteErrorBoundary>
<VoteButton />
</VoteErrorBoundary>DataErrorBoundary
For generic data loading:
import { DataErrorBoundary } from '@/components/ErrorBoundary'
<DataErrorBoundary message="Failed to load analytics data">
<AnalyticsWidget />
</DataErrorBoundary>Architecture
App-Level Boundary
Wrap the entire app in layout:
// app/layout.tsx
<ErrorBoundary>
<WalletProvider>
<ToastProvider>
{children}
</ToastProvider>
</WalletProvider>
</ErrorBoundary>Component-Level Boundaries
Wrap specific features:
<ErrorBoundary>
<Dashboard>
<WalletErrorBoundary>
<ReputationCard />
</WalletErrorBoundary>
<DataErrorBoundary message="Failed to load proposals">
<ActiveProposals />
</DataErrorBoundary>
</Dashboard>
</ErrorBoundary>Error Recovery
Automatic Reset
The “Try Again” button resets error state:
// Internal implementation
<button onClick={() => this.setState({ hasError: false, error: null })}>
Try Again
</button>Manual Recovery
Use key to force remount:
const [retryKey, setRetryKey] = useState(0)
<ErrorBoundary key={retryKey}>
<Component />
</ErrorBoundary>
<button onClick={() => setRetryKey(k => k + 1)}>
Retry
</button>Error Logging
Errors are logged to console:
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('ErrorBoundary caught:', error, errorInfo)
}For production, integrate with error tracking:
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Send to Sentry, LogRocket, etc.
captureException(error, { extra: errorInfo })
}Best Practices
1. Granular Boundaries
Don’t catch everything at once:
// Bad - one error breaks everything
<ErrorBoundary>
<Header />
<Sidebar />
<MainContent />
</ErrorBoundary>
// Good - isolate failures
<>
<ErrorBoundary><Header /></ErrorBoundary>
<ErrorBoundary><Sidebar /></ErrorBoundary>
<ErrorBoundary><MainContent /></ErrorBoundary>
</>2. Contextual Messages
Provide specific error context:
<DataErrorBoundary message="Failed to load voting history">
<VotingHistory />
</DataErrorBoundary>3. Recovery Actions
Offer ways to recover:
// In fallback
<div>
<p>{error.message}</p>
<button onClick={retry}>Retry</button>
<button onClick={goBack}>Go Back</button>
<button onClick={reload}>Reload Page</button>
</div>4. Graceful Degradation
Show partial content when possible:
// Instead of complete failure
{error ? (
<FallbackComponent />
) : (
<FullComponent data={data} />
)}
// Show partial
{error ? (
<PartialComponent fallbackData={cachedData} />
) : (
<FullComponent data={data} />
)}Error States by Feature
| Feature | Boundary | Fallback Message |
|---|---|---|
| Wallet | WalletErrorBoundary | ”Please reconnect wallet” |
| Proposal | ProposalErrorBoundary | ”Proposal may be removed” |
| Vote | VoteErrorBoundary | ”Vote failed to load” |
| Analytics | DataErrorBoundary | ”Failed to load data” |
| Admin | DataErrorBoundary | ”Failed to load admin data” |