# Streamlined Error & Load State Handling - Quick Reference
## 🎯 Goal
Standardize error and loading state handling across all GridPilot website pages using user-friendly, accessible, and consistent shared components.
## 📁 File Structure
```
apps/website/components/shared/
├── state/
│ ├── LoadingWrapper.tsx # Loading states
│ ├── ErrorDisplay.tsx # Error states
│ ├── EmptyState.tsx # Empty states
│ └── StateContainer.tsx # Combined wrapper
├── hooks/
│ └── useDataFetching.ts # Unified data fetching
└── types/
└── state.types.ts # TypeScript interfaces
```
## 🚀 Quick Start Examples
### 1. Basic Page Implementation
```typescript
import { useDataFetching } from '@/components/shared/hooks/useDataFetching';
import { LoadingWrapper } from '@/components/shared/state/LoadingWrapper';
import { ErrorDisplay } from '@/components/shared/state/ErrorDisplay';
import { EmptyState } from '@/components/shared/state/EmptyState';
function MyPage() {
const { data, isLoading, error, retry } = useDataFetching({
queryKey: ['myData'],
queryFn: () => myService.getData(),
});
if (isLoading) return ;
if (error) return ;
if (!data) return ;
return ;
}
```
### 2. Using StateContainer (Recommended)
```typescript
import { StateContainer } from '@/components/shared/state/StateContainer';
function MyPage() {
const { data, isLoading, error, retry } = useDataFetching({
queryKey: ['myData'],
queryFn: () => myService.getData(),
});
return (
{(content) => }
);
}
```
## 🎨 Component Variants
### LoadingWrapper
- `spinner` - Traditional spinner (default)
- `skeleton` - Skeleton screens
- `full-screen` - Centered in viewport
- `inline` - Compact inline
- `card` - Card placeholders
### ErrorDisplay
- `full-screen` - Full page error
- `inline` - Inline error message
- `card` - Error card
- `toast` - Toast notification
### EmptyState
- `default` - Standard empty state
- `minimal` - Simple version
- `full-page` - Full page empty state
## 🔧 useDataFetching Hook
```typescript
const {
data, // The fetched data
isLoading, // Initial load
isFetching, // Any fetch (including refetch)
error, // ApiError or null
retry, // Retry failed request
refetch, // Manual refetch
lastUpdated, // Timestamp
isStale // Cache status
} = useDataFetching({
queryKey: ['uniqueKey', id],
queryFn: () => apiService.getData(id),
enabled: true, // Enable/disable query
retryOnMount: true, // Auto-retry on mount
cacheTime: 5 * 60 * 1000, // 5 minutes
staleTime: 1 * 60 * 1000, // 1 minute
maxRetries: 3,
retryDelay: 1000,
onSuccess: (data) => { /* ... */ },
onError: (error) => { /* ... */ },
});
```
## 📋 Migration Checklist
### Phase 1: Foundation
- [ ] Create `components/shared/state/` directory
- [ ] Implement `LoadingWrapper.tsx`
- [ ] Implement `ErrorDisplay.tsx`
- [ ] Implement `EmptyState.tsx`
- [ ] Implement `StateContainer.tsx`
- [ ] Implement `useDataFetching.ts` hook
- [ ] Create TypeScript interfaces
- [ ] Add comprehensive tests
### Phase 2: Core Pages (High Priority)
- [ ] `app/dashboard/page.tsx`
- [ ] `app/leagues/[id]/LeagueDetailInteractive.tsx`
- [ ] `app/races/[id]/RaceDetailInteractive.tsx`
### Phase 3: Additional Pages
- [ ] `app/teams/[id]/TeamDetailInteractive.tsx`
- [ ] `app/leagues/[id]/schedule/page.tsx`
- [ ] `app/races/[id]/results/RaceResultsInteractive.tsx`
- [ ] `app/races/[id]/stewarding/RaceStewardingInteractive.tsx`
- [ ] Sponsor pages
- [ ] Profile pages
### Phase 4: Cleanup
- [ ] Remove old loading components
- [ ] Remove old error components
- [ ] Update documentation
- [ ] Team training
## 🔄 Before & After
### Before (Inconsistent)
```typescript
function DashboardPage() {
const { data, isLoading, error } = useDashboardOverview();
if (isLoading) {
return (
Loading dashboard...
);
}
if (error || !dashboardData) {
return (
Failed to load dashboard
);
}
return ;
}
```
### After (Standardized)
```typescript
function DashboardPage() {
const { data, isLoading, error, retry } = useDataFetching({
queryKey: ['dashboardOverview'],
queryFn: () => dashboardService.getDashboardOverview(),
});
return (
{(content) => }
);
}
```
## 🎯 Key Benefits
1. **Consistency**: Same patterns across all pages
2. **User-Friendly**: Clear messages and helpful actions
3. **Accessible**: ARIA labels, keyboard navigation
4. **Developer-Friendly**: Simple API, less boilerplate
5. **Maintainable**: Single source of truth
6. **Flexible**: Multiple variants for different needs
7. **Type-Safe**: Full TypeScript support
## 📊 Success Metrics
- ✅ 100% page coverage
- ✅ 60% less error handling code
- ✅ Consistent UX across app
- ✅ Better error recovery
- ✅ Faster development
## 🔗 Related Files
- Design Document: `STREAMLINED_STATE_HANDLING_DESIGN.md`
- TypeScript Interfaces: `components/shared/state/types.ts`
- Component Tests: `components/shared/state/*.test.tsx`
- Hook Tests: `components/shared/hooks/useDataFetching.test.ts`
## 💡 Tips
1. **Always use `useDataFetching`** instead of manual state management
2. **Prefer `StateContainer`** for complex pages
3. **Use `skeleton` variant** for better perceived performance
4. **Enable `retryOnMount`** for recoverable errors
5. **Customize config** per page needs
6. **Test all states**: loading, error, empty, success
## 🆘 Troubleshooting
**Q: Error not showing?**
A: Check if error is instance of `ApiError`
**Q: Loading not visible?**
A: Verify `isLoading` is true and component is rendered
**Q: Retry not working?**
A: Ensure `onRetry` prop is passed to ErrorDisplay
**Q: Empty state not showing?**
A: Check if data is null/undefined and `showEmpty` is true
---
For detailed implementation guide, see: `STREAMLINED_STATE_HANDLING_DESIGN.md`