mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
- Remove nested git repository from bugulma/frontend/.git - Add all frontend files to main repository tracking - Convert from separate frontend/backend repos to unified monorepo - Preserve all frontend code and development history as tracked files - Eliminate nested repository complexity for simpler development workflow This creates a proper monorepo structure with frontend and backend coexisting in the same repository for easier development and deployment.
237 lines
6.6 KiB
Markdown
237 lines
6.6 KiB
Markdown
# Async Rendering Guide
|
|
|
|
## Overview
|
|
|
|
All pages and components are designed to render **asynchronously** - they never block on API requests. Components render immediately with loading states or placeholder data, and data updates asynchronously as it arrives from the backend.
|
|
|
|
## Key Principles
|
|
|
|
### 1. **Non-Blocking Rendering**
|
|
|
|
- Components render immediately, even before API data arrives
|
|
- Use `placeholderData` in React Query hooks to provide safe defaults
|
|
- Never wait for API responses before rendering
|
|
|
|
### 2. **Progressive Data Loading**
|
|
|
|
- Initial render shows loading states or empty states
|
|
- Data updates asynchronously as API responses arrive
|
|
- Multiple API calls happen in parallel, not sequentially
|
|
|
|
### 3. **Safe Defaults**
|
|
|
|
- All hooks return safe default structures (empty arrays, undefined, etc.)
|
|
- Components handle `undefined` and `null` gracefully
|
|
- No assumptions about data structure
|
|
|
|
## Implementation Patterns
|
|
|
|
### React Query Hooks with placeholderData
|
|
|
|
All `useQuery` hooks include `placeholderData` to prevent blocking:
|
|
|
|
```typescript
|
|
// ✅ Good - Renders immediately with empty array
|
|
export function useOrganizations() {
|
|
return useQuery({
|
|
queryKey: organizationKeys.lists(),
|
|
queryFn: getOrganizations,
|
|
placeholderData: [], // Component renders immediately
|
|
staleTime: 30 * 1000,
|
|
});
|
|
}
|
|
|
|
// ✅ Good - Renders immediately with undefined
|
|
export function useOrganization(id: string | null | undefined) {
|
|
return useQuery({
|
|
queryKey: organizationKeys.detail(id!),
|
|
queryFn: () => getOrganizationById(id!),
|
|
enabled: !!id,
|
|
placeholderData: undefined, // Component handles undefined
|
|
});
|
|
}
|
|
```
|
|
|
|
### Component Pattern
|
|
|
|
Components should handle placeholder data gracefully:
|
|
|
|
```typescript
|
|
// ✅ Good - Handles placeholder data
|
|
const MyComponent = () => {
|
|
const { data, isLoading } = useOrganizations();
|
|
|
|
// placeholderData ensures data is always an array
|
|
const organizations = Array.isArray(data) ? data : [];
|
|
|
|
// Render immediately, show loading only if no placeholder data
|
|
if (isLoading && organizations.length === 0) {
|
|
return <LoadingSpinner />;
|
|
}
|
|
|
|
return <div>{/* Render with data */}</div>;
|
|
};
|
|
```
|
|
|
|
### Safe Array Operations
|
|
|
|
Always check arrays before operations:
|
|
|
|
```typescript
|
|
// ✅ Good - Safe array operations
|
|
const items = Array.isArray(data?.items) ? data.items : [];
|
|
const filtered = items.filter(item => item?.id);
|
|
const mapped = items.map(item => ({ ...item }));
|
|
|
|
// ❌ Bad - Assumes data is always an array
|
|
const items = data.items; // Could be undefined
|
|
items.filter(...); // Crashes if undefined
|
|
```
|
|
|
|
## Hooks Updated
|
|
|
|
All API hooks now include `placeholderData`:
|
|
|
|
### Organizations
|
|
|
|
- `useOrganizations()` - `placeholderData: []`
|
|
- `useOrganization(id)` - `placeholderData: undefined`
|
|
- `useUserOrganizations()` - `placeholderData: []`
|
|
|
|
### Sites
|
|
|
|
- `useSite(id)` - `placeholderData: undefined`
|
|
- `useSitesByOrganization(id)` - `placeholderData: []`
|
|
- `useNearbySites(query)` - `placeholderData: []`
|
|
|
|
### Resource Flows
|
|
|
|
- `useResourceFlow(id)` - `placeholderData: undefined`
|
|
- `useResourceFlowsBySite(id)` - `placeholderData: []`
|
|
- `useResourceFlowsByOrganization(id)` - `placeholderData: []`
|
|
|
|
### Proposals
|
|
|
|
- `useProposals()` - `placeholderData: { proposals: [] }`
|
|
- `useProposal(id)` - `placeholderData: undefined`
|
|
- `useProposalsForOrganization(id)` - `placeholderData: { incoming: [], outgoing: [] }`
|
|
|
|
### Matching
|
|
|
|
- `useDirectSymbiosis(id)` - `placeholderData: { providers: [], consumers: [] }`
|
|
- `useFindMatches(id)` - `placeholderData: { matches: [] }`
|
|
|
|
### Analytics
|
|
|
|
- `useConnectionStatistics()` - `placeholderData: { total_connections: 0 }`
|
|
- `useSupplyDemandAnalysis()` - `placeholderData: { supply: [], demand: [] }`
|
|
- `useDashboardStatistics()` - `placeholderData: { total_organizations: 0, recent_activity: [] }`
|
|
|
|
## Benefits
|
|
|
|
1. **Instant Rendering**: Pages render immediately, no waiting for APIs
|
|
2. **Better UX**: Users see loading states or empty states right away
|
|
3. **No Freezing**: Components never block on API requests
|
|
4. **Progressive Enhancement**: Data appears as it loads
|
|
5. **Error Resilience**: Safe defaults prevent crashes
|
|
|
|
## Testing
|
|
|
|
To verify async rendering:
|
|
|
|
1. **Network Throttling**: Use DevTools to throttle network to "Slow 3G"
|
|
2. **Check Rendering**: Page should render immediately with loading states
|
|
3. **Verify Updates**: Data should appear as API responses arrive
|
|
4. **Test Empty States**: Disable network to see empty state handling
|
|
|
|
## Common Patterns
|
|
|
|
### Pattern 1: List with Loading State
|
|
|
|
```typescript
|
|
const { data, isLoading } = useOrganizations();
|
|
const items = Array.isArray(data) ? data : [];
|
|
|
|
if (isLoading && items.length === 0) {
|
|
return <LoadingSpinner />;
|
|
}
|
|
|
|
return <List items={items} />;
|
|
```
|
|
|
|
### Pattern 2: Detail with Placeholder
|
|
|
|
```typescript
|
|
const { data, isLoading } = useOrganization(id);
|
|
|
|
if (isLoading && !data) {
|
|
return <Skeleton />;
|
|
}
|
|
|
|
if (!data) {
|
|
return <NotFound />;
|
|
}
|
|
|
|
return <Detail data={data} />;
|
|
```
|
|
|
|
### Pattern 3: Parallel Loading
|
|
|
|
```typescript
|
|
// Multiple queries load in parallel
|
|
const { data: org } = useOrganization(id);
|
|
const { data: sites } = useSitesByOrganization(id);
|
|
const { data: flows } = useResourceFlowsByOrganization(id);
|
|
|
|
// All render immediately with placeholderData
|
|
// Updates happen asynchronously as data arrives
|
|
```
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
### ❌ Blocking on Data
|
|
|
|
```typescript
|
|
// Bad - Blocks render until data arrives
|
|
const { data } = useOrganizations();
|
|
if (!data) return null; // Blocks render
|
|
```
|
|
|
|
### ❌ Unsafe Array Operations
|
|
|
|
```typescript
|
|
// Bad - Crashes if data is undefined
|
|
const items = data.items;
|
|
items.map(...); // Error if items is undefined
|
|
```
|
|
|
|
### ❌ Synchronous Operations in Render
|
|
|
|
```typescript
|
|
// Bad - Blocks render
|
|
const processed = heavyComputation(data); // Blocks
|
|
```
|
|
|
|
## Performance Considerations
|
|
|
|
1. **placeholderData** is lightweight - just empty arrays/objects
|
|
2. **React Query** handles caching and deduplication
|
|
3. **Components** re-render only when data actually changes
|
|
4. **Memoization** prevents unnecessary recalculations
|
|
|
|
## Migration Checklist
|
|
|
|
- [x] All `useQuery` hooks have `placeholderData`
|
|
- [x] Components handle `undefined` and `null` safely
|
|
- [x] Array operations are guarded with `Array.isArray()`
|
|
- [x] Loading states show only when no placeholder data exists
|
|
- [x] Error states don't block rendering
|
|
- [x] Context providers don't block on data
|
|
|
|
## Future Improvements
|
|
|
|
1. **Suspense Boundaries**: Consider React Suspense for better loading UX
|
|
2. **Optimistic Updates**: Update UI immediately, rollback on error
|
|
3. **Streaming**: For large datasets, consider streaming responses
|
|
4. **Prefetching**: Prefetch data before navigation
|