-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Problem
The OrdersTable component (981 lines) contains complex real-time update orchestration logic mixing SSE, polling, and UI state that should be extracted into a reusable custom hook. This violates separation of concerns and makes the component difficult to understand and test.
Current Complexity Issues
Mixed Concerns in Component:
- Real-time connection management (SSE vs polling modes)
- Update mode persistence (localStorage)
- Connection status indicators
- Polling interval timers
- Manual refresh triggers
- Silent vs non-silent fetch orchestration
- Update timestamp tracking
- Connection fallback logic
Impact:
- Lines 74-100: Update mode configuration and storage helpers (utility logic in component)
- Lines 200-250: Complex SSE hook integration with callbacks and fallbacks
- Lines 252-267: Connection indicator derivation logic
- Lines 268-400: Fetch orchestration with silent mode, refs, and state coordination
- Result: ~150 lines of orchestration logic mixed with presentation code
Architecture Problem
// Current: All orchestration logic in component
export function OrdersTable({ storeId }: OrdersTableProps) {
// ❌ Update mode state and persistence
const [updateMode, setUpdateMode] = useState(UpdateMode)('sse');
// ❌ SSE integration logic
const { status: sseStatus, reconnect: sseReconnect } = useOrderStream({...});
// ❌ Polling state
const [lastCheckedAt, setLastCheckedAt] = useState(string)(...);
// ❌ Fetch orchestration
const fetchOrders = useCallback(async (silent = false) => {...}, [...]);
// ❌ Connection indicator derivation
const getConnectionIndicator = useCallback(() => {...}, [...]);
// ❌ Polling effects
useEffect(() => { /* complex polling setup */ }, [...]);
// ✅ Presentation logic (should be the only concern)
return <Table>...</Table>;
}Current Code Location
- File:
src/components/orders-table.tsx(981 lines) - Orchestration Logic: Lines 74-400 (~326 lines, 33% of component)
- Complexity: High - Multiple state machines, effects, callbacks, refs
Proposed Refactoring
Extract all real-time orchestration logic into a dedicated useOrdersSync custom hook.
Benefits
- Separation of Concerns: Component focuses only on presentation/UI
- Reusability: Can reuse sync logic in other components (OrderDetail, Dashboard widgets)
- Testability: Can unit test orchestration logic independently without mounting component
- Maintainability: Changes to sync strategy don't require modifying component
- Readability: Reduces component complexity from 981 lines to ~650 lines
- Type Safety: Encapsulates sync state with proper TypeScript types
Suggested Approach
Step 1: Create dedicated custom hook
// src/hooks/useOrdersSync.ts
export type UpdateMode = 'sse' | 'realtime' | 'balanced' | 'battery' | 'disabled';
export interface UseOrdersSyncOptions {
storeId: string;
enabled?: boolean;
onDataUpdate?: (orders: Order[]) => void;
onError?: (error: Error) => void;
}
export interface UseOrdersSyncResult {
// Connection state
status: 'connected' | 'connecting' | 'error' | 'disconnected';
updateMode: UpdateMode;
lastSyncedAt: Date | null;
// Actions
setUpdateMode: (mode: UpdateMode) => void;
forceSync: () => Promise(void);
reconnect: () => void;
// UI helpers
connectionIndicator: {
icon: LucideIcon;
color: string;
label: string;
};
}
/**
* Custom hook for orchestrating real-time order updates
* Handles SSE, polling fallback, connection management, and sync state
*/
export function useOrdersSync({
storeId,
enabled = true,
onDataUpdate,
onError,
}: UseOrdersSyncOptions): UseOrdersSyncResult {
// Internal state
const [updateMode, setUpdateModeState] = useState(UpdateMode)(getStoredUpdateMode());
const [lastSyncedAt, setLastSyncedAt] = useState(Date | null)(null);
const [sseEnabled, setSseEnabled] = useState(updateMode === 'sse');
// SSE integration
const {
status: sseStatus,
reconnect: sseReconnect,
} = useOrderStream({
storeId,
enabled: sseEnabled && enabled,
onNewOrders: (event) => {
// Notify parent component
onDataUpdate?.(event.data.orders);
setLastSyncedAt(new Date());
},
onStatusChange: (status) => {
if (status === 'error') {
// Auto-fallback to polling
setUpdateModeInternal('balanced');
}
},
});
// Polling orchestration
useEffect(() => {
if (!enabled || updateMode === 'sse' || updateMode === 'disabled') return;
const interval = UPDATE_MODE_INTERVALS[updateMode];
const timer = setInterval(() => {
checkForUpdates();
}, interval);
return () => clearInterval(timer);
}, [enabled, updateMode, storeId]);
// Update mode persistence
const setUpdateMode = useCallback((mode: UpdateMode) => {
setUpdateModeState(mode);
setSseEnabled(mode === 'sse');
setStoredUpdateMode(mode);
}, []);
// Force sync (manual refresh)
const forceSync = useCallback(async () => {
try {
// Implementation
const orders = await fetchOrders();
onDataUpdate?.(orders);
setLastSyncedAt(new Date());
} catch (error) {
onError?.(error as Error);
}
}, [storeId, onDataUpdate, onError]);
// Connection indicator
const connectionIndicator = useMemo(() => {
if (updateMode !== 'sse') {
return { icon: Clock, color: 'text-muted-foreground', label: 'Polling' };
}
switch (sseStatus) {
case 'connected':
return { icon: Wifi, color: 'text-green-500', label: 'Live' };
case 'connecting':
return { icon: Wifi, color: 'text-yellow-500 animate-pulse', label: 'Connecting...' };
case 'error':
return { icon: WifiOff, color: 'text-red-500', label: 'Disconnected' };
default:
return { icon: WifiOff, color: 'text-muted-foreground', label: 'Offline' };
}
}, [updateMode, sseStatus]);
return {
status: sseEnabled ? sseStatus : 'disconnected',
updateMode,
lastSyncedAt,
setUpdateMode,
forceSync,
reconnect: sseReconnect,
connectionIndicator,
};
}Step 2: Simplify OrdersTable component
// src/components/orders-table.tsx (simplified)
export function OrdersTable({ storeId }: OrdersTableProps) {
const [orders, setOrders] = useState(Order[])([]);
const [loading, setLoading] = useState(false);
// ✅ All orchestration logic encapsulated in hook
const {
status,
updateMode,
setUpdateMode,
forceSync,
connectionIndicator,
} = useOrdersSync({
storeId,
onDataUpdate: (newOrders) => {
setOrders(newOrders);
toast.info(`\$\{newOrders.length} new orders`);
},
onError: (error) => {
toast.error('Failed to sync orders');
},
});
// ✅ Component focuses on presentation
return (
(Card)
(CardHeader)
(div className="flex items-center justify-between")
(CardTitle)Orders(/CardTitle)
(UpdateModeSelector
mode={updateMode}
onChange={setUpdateMode}
connectionIndicator={connectionIndicator}
/)
(/div)
(/CardHeader)
(CardContent)
(DataTable data={orders} columns={columns} /)
(/CardContent)
(/Card)
);
}Step 3: Create comprehensive tests
// src/hooks/__tests__/useOrdersSync.test.ts
describe('useOrdersSync', () => {
it('should use SSE by default', () => {
const { result } = renderHook(() => useOrdersSync({ storeId: 'test' }));
expect(result.current.updateMode).toBe('sse');
});
it('should fallback to polling when SSE fails', async () => {
// Mock SSE failure
// ...
await waitFor(() => {
expect(result.current.updateMode).toBe('balanced');
});
});
it('should persist update mode to localStorage', () => {
// ...
});
it('should call onDataUpdate when new orders arrive', () => {
// ...
});
// ... more tests
});Code Example Summary
Before: 981-line component with 326 lines of orchestration logic
After: ~30-line hook usage + 650-line focused component
Net improvement:
- Component: 981 → 650 lines (33% reduction)
- Reusable hook: Can be used in other components
- Testability: Independent testing of sync logic
Impact Assessment
- Effort: Medium - Requires careful state extraction and effect migration
- Risk: Medium - Critical component, needs thorough testing
- Benefit: High - Major improvement in maintainability and reusability
- Priority: High - Component is central to dashboard functionality
Related Files
src/components/orders-table.tsx(lines 74-400)src/hooks/useOrderStream.ts(already exists, will integrate)- New:
src/hooks/useOrdersSync.ts
Testing Strategy
- Create comprehensive unit tests for
useOrdersSynchook - Test SSE connection scenarios (success, failure, reconnection)
- Test polling mode transitions
- Test localStorage persistence
- Integration test with OrdersTable component
- Verify real-time updates in development environment
- Load test with concurrent updates
Implementation Order
- ✅ Create
useOrdersSynchook with core functionality - ✅ Add unit tests for hook
- ✅ Create integration tests with mocked OrderStream
- ✅ Refactor OrdersTable to use hook
- ✅ Test all update modes (SSE, polling variants)
- ✅ Verify in staging with real SSE server
- ✅ Monitor production metrics after deployment
Future Enhancements
After this refactoring, useOrdersSync can be:
- Reused in
OrderDetailPagefor live order status updates - Reused in
DashboardWidgetfor order count badge - Extended to support other real-time entities (products, inventory)
- Enhanced with offline queue and conflict resolution
AI generated by Daily Codebase Analyzer - Semantic Function Extraction & Refactoring
- expires on Mar 5, 2026, 2:07 PM UTC
Metadata
Metadata
Assignees
Type
Projects
Status