-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Problem
The orders-table.tsx component contains 981 lines and manages multiple complex concerns in a single file:
- Real-time update system: SSE + polling fallback with connection management
- Data fetching: Orders API calls with complex filtering
- Table rendering: TanStack Table with sorting, pagination
- Filter management: Search, status, payment status, date range
- Update mode UI: Dropdown with 5 different modes (SSE, polling intervals, manual)
- Connection status: Real-time indicator with icons and tooltips
- Order operations: View, cancel, download invoice actions
- Responsive design: Mobile and desktop layouts
This violates the Single Responsibility Principle and creates several issues:
- Poor testability: Testing requires mocking SSE, API calls, table logic, and UI interactions
- Difficult maintenance: Changes to one concern affect unrelated code
- High cognitive load: Developers must understand 8+ different responsibilities
- Reusability limitations: Real-time update logic is locked in this component
- Bundle size: Large client component increases initial JavaScript load
Current Code Location
- File:
src/components/orders-table.tsx(981 lines) - Component:
OrdersTable - Complexity: Very High - manages SSE, API, table, filters, UI state
Proposed Refactoring
Decompose into focused, reusable components and hooks
Split the monolithic component into a layered architecture:
┌─────────────────────────────────────┐
│ OrdersTable (Container) │ ← Orchestrates child components
│ • Minimal state coordination │
│ • Layout composition │
└─────────────────────────────────────┘
↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Filters │ │ Updates │ │ Actions │
│ Bar │ │ Panel │ │ Menu │
└──────────┘ └──────────┘ └──────────┘
↓
┌──────────────────────────┐
│ OrdersTableView │ ← Pure table rendering
│ • TanStack Table only │
└──────────────────────────┘
↑
┌──────────────────────────┐
│ useOrdersData Hook │ ← Data fetching + filtering
│ • API calls │
│ • Filter state │
│ • Pagination │
└──────────────────────────┘
↑
┌──────────────────────────┐
│ useRealtimeOrders Hook │ ← Real-time updates
│ • SSE connection │
│ • Polling fallback │
│ • Update mode │
└──────────────────────────┘
Benefits
- Separation of Concerns: Each component/hook has a single, clear responsibility
- Testability: Test SSE logic, API calls, table rendering, and filters independently
- Reusability:
useRealtimeOrderscan be used in dashboard widgets, notification systems, etc. - Maintainability: Changes to real-time logic don't affect table rendering
- Performance: Smaller components enable better React memoization
- Code Splitting: Table logic can be lazy-loaded for mobile views
- Type Safety: Focused interfaces reduce type complexity
Suggested Approach
Step 1: Extract Real-time Update Hook
Create src/hooks/useRealtimeOrders.ts:
/**
* Manages real-time order updates with SSE + polling fallback
*
* `@param` storeId - Store ID to monitor
* `@param` onOrdersUpdated - Callback when orders change
* `@returns` Connection state and controls
*/
export function useRealtimeOrders({
storeId,
onOrdersUpdated,
}: {
storeId: string;
onOrdersUpdated: () => void;
}) {
// SSE connection logic
// Polling fallback logic
// Update mode management
// Connection status tracking
return {
status: 'connected' | 'connecting' | 'error' | 'disconnected',
updateMode: 'sse' | 'realtime' | 'balanced' | 'battery' | 'disabled',
setUpdateMode: (mode) => void,
reconnect: () => void,
};
}Step 2: Extract Data Fetching Hook
Create src/hooks/useOrdersData.ts:
/**
* Manages orders data fetching with filtering and pagination
*
* `@param` storeId - Store ID to fetch orders for
* `@returns` Orders data, loading state, and filter controls
*/
export function useOrdersData({ storeId }: { storeId: string }) {
// API call logic
// Filter state management
// Pagination logic
return {
orders: Order[],
loading: boolean,
error: Error | null,
filters: FilterState,
setFilters: (filters) => void,
pagination: PaginationState,
refetch: (silent?: boolean) => Promise(void),
};
}Step 3: Extract Filter Components
Create src/components/orders/filters/:
OrdersFilterBar.tsx- Main filter bar with search, status, datesOrderStatusFilter.tsx- Status dropdownPaymentStatusFilter.tsx- Payment status dropdownDateRangeFilter.tsx- Date range picker
Step 4: Extract Update Mode Panel
Create src/components/orders/OrdersUpdatePanel.tsx:
/**
* Update mode selector with connection status indicator
*/
export function OrdersUpdatePanel({
updateMode,
onUpdateModeChange,
connectionStatus,
onReconnect,
}: OrdersUpdatePanelProps) {
// Dropdown with SSE/polling modes
// Connection status indicator
// Reconnect button
}Step 5: Extract Table View
Create src/components/orders/OrdersTableView.tsx:
/**
* Pure table rendering component
* Only responsible for displaying orders with TanStack Table
*/
export function OrdersTableView({
orders,
loading,
onSort,
onViewOrder,
onCancelOrder,
}: OrdersTableViewProps) {
// TanStack Table setup
// Column definitions
// Row rendering
}Step 6: Refactor Container
Simplify src/components/orders-table.tsx to orchestrate child components:
export function OrdersTable({ storeId }: { storeId: string }) {
const { orders, loading, filters, setFilters, pagination, refetch } =
useOrdersData({ storeId });
const { status, updateMode, setUpdateMode, reconnect } =
useRealtimeOrders({ storeId, onOrdersUpdated: refetch });
return (
(div)
(OrdersFilterBar filters={filters} onFiltersChange={setFilters} /)
(OrdersUpdatePanel
updateMode={updateMode}
onUpdateModeChange={setUpdateMode}
connectionStatus={status}
onReconnect={reconnect}
/)
(OrdersTableView
orders={orders}
loading={loading}
onSort={handleSort}
onViewOrder={handleViewOrder}
onCancelOrder={handleCancelOrder}
/)
(/div)
);
}Code Example
Before (981 lines in one file):
export function OrdersTable({ storeId }: { storeId: string }) {
// 40+ lines of state declarations
const [updateMode, setUpdateMode] = useState(UpdateMode)('sse');
const [orders, setOrders] = useState(Order[])([]);
const [searchQuery, setSearchQuery] = useState('');
const [statusFilter, setStatusFilter] = useState(string)('all');
// ... 10+ more useState calls
// 100+ lines of SSE logic
const { status: sseStatus, reconnect } = useOrderStream({
storeId,
enabled: updateMode === 'sse',
onNewOrders: (event) => { /* ... */ },
});
// 150+ lines of data fetching
const fetchOrders = useCallback(async () => { /* ... */ }, []);
// 200+ lines of table setup
const columns = useMemo(ColumnDef<Order)[]>(() => [...], []);
// 400+ lines of JSX with filters, indicators, table, actions
return ( /* ... */ );
}After (Container - ~150 lines):
import { useRealtimeOrders } from '@/hooks/useRealtimeOrders';
import { useOrdersData } from '@/hooks/useOrdersData';
import { OrdersFilterBar } from './orders/filters/OrdersFilterBar';
import { OrdersUpdatePanel } from './orders/OrdersUpdatePanel';
import { OrdersTableView } from './orders/OrdersTableView';
export function OrdersTable({ storeId }: { storeId: string }) {
const { orders, loading, filters, setFilters, pagination, refetch } =
useOrdersData({ storeId });
const { status, updateMode, setUpdateMode, reconnect } =
useRealtimeOrders({ storeId, onOrdersUpdated: refetch });
return (
(Card)
(CardHeader)
(div className="flex items-center justify-between")
(CardTitle)Orders(/CardTitle)
(OrdersUpdatePanel
updateMode={updateMode}
onUpdateModeChange={setUpdateMode}
connectionStatus={status}
onReconnect={reconnect}
/)
(/div)
(/CardHeader)
(CardContent)
(OrdersFilterBar filters={filters} onFiltersChange={setFilters} /)
(OrdersTableView
orders={orders}
loading={loading}
pagination={pagination}
onSort={handleSort}
onViewOrder={handleViewOrder}
onCancelOrder={handleCancelOrder}
/)
(/CardContent)
(/Card)
);
}Impact Assessment
- Effort: High - Requires creating 6+ new files and significant refactoring
- Risk: Medium - Complex logic migration requires careful testing
- Benefit: Very High - Dramatically improves maintainability, testability, and reusability
- Priority: High - High-traffic component with ongoing feature development
Related Files
src/components/orders-table.tsx(primary file - 981 lines)src/hooks/useOrderStream.ts(SSE hook - can be absorbed intouseRealtimeOrders)src/components/orders/order-filters.tsx(existing filters - needs extraction)src/components/orders/orders-table-skeleton.tsx(loading state)src/components/orders/cancel-order-dialog.tsx(action dialog)
Testing Strategy
- Unit tests: Test each hook independently with mocked dependencies
- Component tests: Test each UI component in isolation
- Integration tests: Test container composition with real hooks
- E2E tests: Verify SSE connection, filtering, sorting, and actions work end-to-end
- Performance tests: Measure render performance before/after refactoring
Migration Strategy
To minimize risk, implement incremental migration:
- Create new hook files alongside existing component
- Extract one concern at a time (filters → update panel → table view → data → real-time)
- Run tests after each extraction
- Use feature flags to toggle between old/new implementation
- Monitor production metrics during rollout
Additional Context
This refactoring follows Clean Architecture and Component Composition patterns. The layered approach separates:
- Presentation (OrdersTableView, filters, panels)
- Business Logic (useOrdersData, useRealtimeOrders hooks)
- Infrastructure (API calls, SSE connections)
Similar patterns can be applied to other large components:
product-form.tsx(857 lines)product-edit-form.tsx(886 lines)order-detail-client.tsx(861 lines)
AI generated by Daily Codebase Analyzer - Semantic Function Extraction & Refactoring
- expires on Mar 7, 2026, 1:40 PM UTC
Metadata
Metadata
Assignees
Type
Projects
Status