Skip to content

[Frontend] Refactor useTaskMonitor Hook: Decompose into WebSocket, NodeState, Payments, and Outputs Hooks #167

Description

@devJaja

🎯 Objective

Decompose the monolithic 228-line useTaskMonitor hook into focused, testable sub-hooks following the Single Responsibility Principle.


📁 Files to Modify

Action File Path Description
Modify frontend/src/hooks/useTaskMonitor.ts Refactor into composition of sub-hooks

📁 Files to Create

Action File Path Description
Create frontend/src/hooks/useTaskWebSocket.ts WebSocket connection management
Create frontend/src/hooks/useNodeState.ts Node state machine and transitions
Create frontend/src/hooks/useTaskPayments.ts Payment status tracking
Create frontend/src/hooks/useTaskOutputs.ts Output aggregation from completed nodes

📁 Files to Create (Tests)

Action File Path Description
Create frontend/src/hooks/useTaskWebSocket.test.ts WebSocket hook tests
Create frontend/src/hooks/useNodeState.test.ts Node state hook tests
Create frontend/src/hooks/useTaskPayments.test.ts Payments hook tests

📁 Files to Modify (Tests)

Action File Path Description
Modify frontend/src/pages/TaskDetailPage.tsx Update import if API changes

🔍 Current Monolithic Structure (useTaskMonitor.ts — 228 lines)

function useTaskMonitor(taskId: string) {
  // 1. WebSocket connection + reconnect (lines 1-60)
  // 2. Node state management (lines 61-120)
  // 3. Payment tracking (lines 121-160)
  // 4. Output aggregation (lines 161-190)
  // 5. Mock data fallback (lines 191-210)
  // 6. Error handling (lines 211-228)

  return { task, nodes, payments, outputs, isConnected, error };
}

✅ Expected Decomposition

1. frontend/src/hooks/useTaskWebSocket.ts

interface UseTaskWebSocketOptions {
  taskId: string;
  onMessage: (event: DAGEvent) => void;
  onConnect?: () => void;
  onDisconnect?: () => void;
}

function useTaskWebSocket(options: UseTaskWebSocketOptions) {
  // WebSocket connection
  // Exponential backoff reconnection
  // Cursor-based replay
  // Heartbeat management

  return { isConnected, reconnect, disconnect };
}

2. frontend/src/hooks/useNodeState.ts

function useNodeState(taskId: string) {
  // Node state machine (pending → running → completed/failed)
  // State transitions from DAGEvent
  // Current node status map

  return { nodes, getNodeStatus, getCompletedNodes };
}

3. frontend/src/hooks/useTaskPayments.ts

function useTaskPayments(taskId: string) {
  // Payment status tracking per node
  // Payment history
  // Total cost calculation

  return { payments, totalCost, getNodePayment };
}

4. frontend/src/hooks/useTaskOutputs.ts

function useTaskOutputs(taskId: string) {
  // Aggregate outputs from completed nodes
  // Final merged result
  // Output formatting

  return { outputs, finalResult, getNodeOutput };
}

5. Refactored useTaskMonitor.ts (Composition)

function useTaskMonitor(taskId: string) {
  const [lastEvent, setLastEvent] = useState<DAGEvent | null>(null);

  const { isConnected } = useTaskWebSocket({
    taskId,
    onMessage: setLastEvent,
  });

  const { nodes } = useNodeState(taskId);
  const { payments } = useTaskPayments(taskId);
  const { outputs, finalResult } = useTaskOutputs(taskId);

  return { task, nodes, payments, outputs, finalResult, isConnected, error };
}

📁 Reference Files

File Path Purpose
frontend/src/hooks/useTaskMonitor.ts Current 228-line monolith to decompose
frontend/src/pages/TaskDetailPage.tsx Consumer of useTaskMonitor
frontend/src/types/api.ts DAGEvent type definition
frontend/src/services/api.ts API client

📋 Migration Strategy

  1. Create the 4 new sub-hooks
  2. Write tests for each sub-hook
  3. Refactor useTaskMonitor to compose sub-hooks
  4. Verify TaskDetailPage still works
  5. Remove mock data fallback from production code
  6. Remove old inline logic from useTaskMonitor

✅ Acceptance Criteria

  • Create useTaskWebSocket hook with connection management
  • Create useNodeState hook with state machine logic
  • Create useTaskPayments hook for payment tracking
  • Create useTaskOutputs hook for output aggregation
  • Refactor useTaskMonitor to compose all 4 sub-hooks
  • Remove mock data fallback for mock-task-e2e-123
  • Create unit tests for each sub-hook
  • Verify TaskDetailPage renders correctly
  • Run npm test — all tests pass
  • Run npm run build — no TypeScript errors

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions