# 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 ; } return
{/* Render with data */}
; }; ``` ### 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 ; } return ; ``` ### Pattern 2: Detail with Placeholder ```typescript const { data, isLoading } = useOrganization(id); if (isLoading && !data) { return ; } if (!data) { return ; } return ; ``` ### 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