Files
frontend/src/hooks/usePollTaskStatus.js

99 lines
2.8 KiB
JavaScript

import { useState, useRef, useCallback } from 'react';
import { fetchWithAuth } from '../fetchWithAuth';
const API_BASE_URL = import.meta.env.VITE_EFC_API_URL;
// Estados que indican que la tarea ya terminó (no hay más que esperar)
const FINAL_STATES = new Set(['SUCCESS', 'FAILURE', 'completed', 'failed', 'cancelled']);
/**
* Polling acotado de estado de tarea.
*
* Uso:
* const { taskState, polling, poll, reset } = usePollTaskStatus({ maxAttempts: 3, intervalMs: 2500 });
*
* // Después de enviar la tarea al microservicio:
* poll(taskId);
*
* // taskState: null | { status, message, error, attempts }
* // polling: true mientras hay intentos pendientes
*/
export function usePollTaskStatus({ maxAttempts = 3, intervalMs = 2500 } = {}) {
const [taskState, setTaskState] = useState(null);
const [polling, setPolling] = useState(false);
const timeoutRef = useRef(null);
const abortedRef = useRef(false);
const attemptsRef = useRef(0);
const stop = useCallback(() => {
abortedRef.current = true;
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
setPolling(false);
}, []);
const reset = useCallback(() => {
stop();
setTaskState(null);
attemptsRef.current = 0;
}, [stop]);
const poll = useCallback((taskId) => {
if (!taskId) return;
// Reiniciar estado previo
abortedRef.current = false;
attemptsRef.current = 0;
setPolling(true);
setTaskState({ status: 'PENDING', message: null, error: null, attempts: 0 });
const attempt = async () => {
if (abortedRef.current) return;
attemptsRef.current += 1;
const n = attemptsRef.current;
try {
const res = await fetchWithAuth(`${API_BASE_URL}/tasks/status/${taskId}/`);
if (abortedRef.current) return;
if (!res.ok) {
setTaskState({ status: 'FAILURE', message: `Error HTTP ${res.status}`, error: true, attempts: n });
setPolling(false);
return;
}
const data = await res.json();
if (abortedRef.current) return;
const newState = {
status: data.status,
message: data.message || data.error || null,
error: data.error || null,
result: data.result || null,
attempts: n,
};
setTaskState(newState);
if (FINAL_STATES.has(data.status) || n >= maxAttempts) {
setPolling(false);
return;
}
timeoutRef.current = setTimeout(attempt, intervalMs);
} catch (err) {
if (abortedRef.current) return;
setTaskState({ status: 'FAILURE', message: err.message, error: true, attempts: n });
setPolling(false);
}
};
attempt();
}, [maxAttempts, intervalMs]);
return { taskState, polling, poll, stop, reset };
}