@mirawision/reactive-hooks
Version:
A comprehensive collection of 50+ React hooks for state management, UI interactions, device APIs, async operations, drag & drop, audio/speech, and more. Full TypeScript support with SSR safety.
96 lines (95 loc) • 3.34 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueueCancelledError = void 0;
exports.useAsyncQueue = useAsyncQueue;
const react_1 = require("react");
class QueueCancelledError extends Error {
constructor() {
super('Queue was cancelled');
this.name = 'QueueCancelledError';
}
}
exports.QueueCancelledError = QueueCancelledError;
/**
* A hook that manages a queue of async tasks and executes them sequentially.
* Tasks are executed in FIFO order, and each task starts only after the previous one completes.
* If a task fails, the error is propagated to the caller but the queue continues processing.
*
* @returns Object containing:
* - enqueue: Function to add a task to the queue
* - size: Current number of pending tasks
* - running: Whether the queue is currently processing tasks
* - clear: Function to clear all pending tasks
*
* @example
* const queue = useAsyncQueue();
* await queue.enqueue(() => api.saveItem(item));
* await queue.enqueue(() => api.updateStatus(status));
*/
function useAsyncQueue() {
const [size, setSize] = (0, react_1.useState)(0);
const [running, setRunning] = (0, react_1.useState)(false);
// Use refs to maintain queue state across renders
const queueRef = (0, react_1.useRef)([]);
const processingRef = (0, react_1.useRef)(false);
const mountedRef = (0, react_1.useRef)(true);
// Process queue items sequentially
const processQueue = (0, react_1.useCallback)(async () => {
if (processingRef.current || queueRef.current.length === 0) {
return;
}
processingRef.current = true;
setRunning(true);
while (queueRef.current.length > 0 && mountedRef.current) {
const { task, resolve, reject } = queueRef.current[0];
try {
const result = await task();
if (mountedRef.current) {
resolve(result);
}
else {
reject(new QueueCancelledError());
}
}
catch (error) {
reject(error);
}
if (mountedRef.current) {
queueRef.current.shift();
setSize(queueRef.current.length);
}
}
processingRef.current = false;
setRunning(false);
}, []);
// Add task to queue
const enqueue = (0, react_1.useCallback)((task) => {
return new Promise((resolve, reject) => {
queueRef.current.push({ task, resolve, reject });
setSize(queueRef.current.length);
processQueue();
});
}, [processQueue]);
// Clear all pending tasks
const clear = (0, react_1.useCallback)(() => {
const error = new QueueCancelledError();
queueRef.current.forEach(({ reject }) => reject(error));
queueRef.current = [];
setSize(0);
}, []);
// Cleanup on unmount
(0, react_1.useEffect)(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
const error = new QueueCancelledError();
queueRef.current.forEach(({ reject }) => reject(error));
};
}, []);
return {
enqueue,
size,
running,
clear,
};
}