React Integration
Complete guide to using Reliable Queue with React applications, including hooks, context, and real-time updates.
Quick Navigation
Basic Usage
The useReliableQueue
hook provides a simple way to integrate queue functionality into your React components.
import React from 'react';import { useReliableQueue } from '@aplanka/reliable-queue';function TaskManager() {const {tasks,stats,addTask,retryTask,clearCompleted} = useReliableQueue({queueName: 'my-tasks',maxRetries: 3,concurrency: 2,processor: async (data) => {// Your task processing logicconsole.log('Processing:', data);await fetch('/api/process', {method: 'POST',body: JSON.stringify(data),});},});return (<div className="p-6"><div className="mb-4"><h2>Queue Statistics</h2><p>Total: {stats.total} | Pending: {stats.pending} | Failed: {stats.failed}</p></div><buttononClick={() => addTask({ message: 'Hello World!' })}className="mb-4 px-4 py-2 bg-blue-500 text-white rounded">Add Task</button><div className="space-y-2">{tasks.map(task => (<div key={task.id} className="p-3 border rounded flex justify-between items-center"><div><span className={`inline-block w-2 h-2 rounded-full mr-2 ${task.status === 'completed' ? 'bg-green-500' :task.status === 'failed' ? 'bg-red-500' :task.status === 'processing' ? 'bg-blue-500' : 'bg-yellow-500'}`}></span>{task.status} - {JSON.stringify(task.data)}</div>{task.status === 'failed' && (<buttononClick={() => retryTask(task.id)}className="px-2 py-1 bg-orange-500 text-white rounded text-sm">Retry</button>)}</div>))}</div></div>);}export default TaskManager;
Automatic Processing
Tasks are processed automatically when you provide a processor function to the hook.
Reactive Updates
Component re-renders automatically when queue state changes, keeping your UI in sync.
Full Configuration
All queue configuration options are available through the hook options.
Hook API Reference
Hook Options
Configuration options for useReliableQueue
queueName?
Name for the queue (enables sharing across components)
Default: undefined
processor?
Function to process tasks
Default: undefined
useSingleton?
Use singleton QueueManager for named queues
Default: true
maxRetries?
Maximum retry attempts
Default: 3
retryDelay?
Base retry delay in milliseconds
Default: 1000
exponentialBackoff?
Use exponential backoff for retries
Default: true
concurrency?
Maximum concurrent tasks
Default: 1
persistent?
Persist queue to localStorage
Default: false
Return Values
Values returned by the hook
tasks
QueuedTask<T>[]
Array of all tasks in the queue
stats
QueueStats
Current queue statistics
addTask
(data: T, options?: AddTaskOptions) => string
Add a task and return its ID
removeTask
(taskId: string) => boolean
Remove a task by ID
retryTask
(taskId: string) => boolean
Retry a failed task
retryAllTasks
() => number
Retry all failed tasks
clearCompleted
() => number
Clear completed tasks
clearFailed
() => number
Clear failed tasks
clearAll
() => void
Clear all tasks
getTask
(taskId: string) => QueuedTask<T> | undefined
Get task by ID
setProcessor
(processor: TaskProcessor<T>) => void
Set task processor
queue
ReliableQueue<T>
Direct access to queue instance
Advanced Hook Usage
Build sophisticated queue-powered components with full type safety and advanced features.
import React, { useState, useCallback } from 'react';import { useReliableQueue } from '@aplanka/reliable-queue';function EmailQueueManager() {const [emailData, setEmailData] = useState({ to: '', subject: '', body: '' });const {tasks,stats,addTask,retryTask,retryAllTasks,clearCompleted,clearFailed,queue // Direct access to queue instance} = useReliableQueue<EmailTask>({queueName: 'email-queue',maxRetries: 5,retryDelay: 2000,exponentialBackoff: true,concurrency: 3,persistent: true,storageKey: 'app-email-queue',processor: async (data, task) => {console.log(`Sending email to ${data.to}: ${data.subject}`);// Simulate email sendingconst response = await fetch('/api/send-email', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(data)});if (!response.ok) {throw new Error(`Email failed: ${response.statusText}`);}console.log(`Email sent successfully to ${data.to}`);}});const handleAddEmail = useCallback(() => {if (!emailData.to || !emailData.subject) return;addTask(emailData, {priority: emailData.to.includes('urgent') ? 10 : 5,delay: 0});setEmailData({ to: '', subject: '', body: '' });}, [emailData, addTask]);const handleBulkActions = useCallback(() => {// Demonstrate bulk operationsretryAllTasks();setTimeout(() => clearCompleted(), 1000);}, [retryAllTasks, clearCompleted]);return (<div className="max-w-4xl mx-auto p-6">{/* Queue Statistics Dashboard */}<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8"><div className="bg-blue-50 p-4 rounded-lg text-center"><div className="text-2xl font-bold text-blue-600">{stats.total}</div><div className="text-sm text-gray-600">Total</div></div><div className="bg-yellow-50 p-4 rounded-lg text-center"><div className="text-2xl font-bold text-yellow-600">{stats.pending}</div><div className="text-sm text-gray-600">Pending</div></div><div className="bg-purple-50 p-4 rounded-lg text-center"><div className="text-2xl font-bold text-purple-600">{stats.processing}</div><div className="text-sm text-gray-600">Processing</div></div><div className="bg-green-50 p-4 rounded-lg text-center"><div className="text-2xl font-bold text-green-600">{stats.completed}</div><div className="text-sm text-gray-600">Completed</div></div><div className="bg-red-50 p-4 rounded-lg text-center"><div className="text-2xl font-bold text-red-600">{stats.failed}</div><div className="text-sm text-gray-600">Failed</div></div></div>{/* Email Form */}<div className="bg-white border rounded-lg p-6 mb-6"><h3 className="text-lg font-semibold mb-4">Send Email</h3><div className="space-y-4"><inputtype="email"placeholder="To"value={emailData.to}onChange={(e) => setEmailData(prev => ({ ...prev, to: e.target.value }))}className="w-full p-2 border rounded"/><inputtype="text"placeholder="Subject"value={emailData.subject}onChange={(e) => setEmailData(prev => ({ ...prev, subject: e.target.value }))}className="w-full p-2 border rounded"/><textareaplaceholder="Body"value={emailData.body}onChange={(e) => setEmailData(prev => ({ ...prev, body: e.target.value }))}className="w-full p-2 border rounded h-24"/><div className="flex gap-2"><button onClick={handleAddEmail} className="px-4 py-2 bg-blue-500 text-white rounded">Queue Email</button><button onClick={handleBulkActions} className="px-4 py-2 bg-gray-500 text-white rounded">Bulk Actions</button></div></div></div>{/* Task List */}<div className="space-y-2">{tasks.map(task => (<EmailTaskItem key={task.id} task={task} onRetry={retryTask} />))}</div></div>);}
Context Pattern
Share queues across multiple components using React Context for global state management.
import React, { createContext, useContext, useEffect, useState } from 'react';import { ReliableQueue, QueueManager } from '@aplanka/reliable-queue';// Create Queue Contextconst QueueContext = createContext();// Queue Provider Componentexport function QueueProvider({ children }) {const [manager] = useState(() => QueueManager.getInstance());const [queues, setQueues] = useState({});const createQueue = (name, config) => {const queue = manager.createQueue(name, config);setQueues(prev => ({ ...prev, [name]: queue }));return queue;};const getQueue = (name) => {return queues[name] || manager.getQueue(name);};return (<QueueContext.Provider value={{manager,queues,createQueue,getQueue}}>{children}</QueueContext.Provider>);}// Custom hook to use queue contextexport function useQueue(name) {const context = useContext(QueueContext);if (!context) {throw new Error('useQueue must be used within QueueProvider');}return context.getQueue(name);}// App with Queue Providerfunction App() {return (<QueueProvider><EmailManager /><ImageProcessor /><NotificationCenter /></QueueProvider>);}// Components using shared queuesfunction EmailManager() {const emailQueue = useQueue('emails');// Use the shared email queue}function ImageProcessor() {const imageQueue = useQueue('images');// Use the shared image queue}
Benefits of Context Pattern
- • Share queue instances across multiple components
- • Centralized queue management and configuration
- • Consistent queue naming and lifecycle management
- • Better performance through singleton pattern
Custom Queue Hooks
Create domain-specific hooks that encapsulate queue logic for specific use cases.
import { useReliableQueue } from '@aplanka/reliable-queue';import { useCallback, useMemo } from 'react';// Custom hook for file uploadsexport function useFileUploadQueue() {const {tasks,stats,addTask,retryTask,removeTask,queue} = useReliableQueue({queueName: 'file-uploads',maxRetries: 3,concurrency: 2,persistent: true,processor: async (fileData) => {const formData = new FormData();formData.append('file', fileData.file);formData.append('metadata', JSON.stringify(fileData.metadata));const response = await fetch('/api/upload', {method: 'POST',body: formData});if (!response.ok) {throw new Error(`Upload failed: ${response.statusText}`);}return response.json();}});const uploadFile = useCallback((file, metadata = {}) => {return addTask({ file, metadata, fileName: file.name });}, [addTask]);const uploadFiles = useCallback((files, metadata = {}) => {return files.map(file => uploadFile(file, metadata));}, [uploadFile]);const cancelUpload = useCallback((taskId) => {return removeTask(taskId);}, [removeTask]);const uploadProgress = useMemo(() => {if (stats.total === 0) return 0;return (stats.completed / stats.total) * 100;}, [stats.completed, stats.total]);const activeUploads = useMemo(() => {return tasks.filter(task =>task.status === 'pending' || task.status === 'processing');}, [tasks]);return {// Original hook datatasks,stats,retryTask,// Custom upload functionsuploadFile,uploadFiles,cancelUpload,// Computed valuesuploadProgress,activeUploads,// Status helpersisUploading: activeUploads.length > 0,hasFailedUploads: stats.failed > 0};}// Usage in componentfunction FileUploader() {const {tasks,uploadFile,uploadFiles,cancelUpload,retryTask,uploadProgress,isUploading,hasFailedUploads} = useFileUploadQueue();const handleFileSelect = (files) => {uploadFiles(Array.from(files));};return (<div className="p-6"><div className="mb-4"><div className="flex justify-between items-center mb-2"><span>Upload Progress</span><span>{uploadProgress.toFixed(0)}%</span></div><div className="w-full bg-gray-200 rounded-full h-2"><divclassName="bg-blue-600 h-2 rounded-full transition-all duration-300"style={{ width: `${uploadProgress}%` }}/></div></div><inputtype="file"multipleonChange={(e) => handleFileSelect(e.target.files)}className="mb-4"/>{isUploading && (<div className="mb-4 p-2 bg-blue-50 rounded">Uploading files...</div>)}{hasFailedUploads && (<div className="mb-4 p-2 bg-red-50 rounded">Some uploads failed. Click retry to try again.</div>)}<div className="space-y-2">{tasks.map(task => (<FileUploadItemkey={task.id}task={task}onCancel={cancelUpload}onRetry={retryTask}/>))}</div></div>);}
Advantages
- • Encapsulate domain-specific logic
- • Provide convenient helper functions
- • Add computed values and state
- • Improve reusability across components
Use Cases
- • File upload queues with progress tracking
- • Email sending with delivery status
- • Image processing pipelines
- • API call batching and retries
Real-time Updates
Build live dashboards and real-time interfaces using queue events and automatic re-renders.
import React, { useState, useEffect } from 'react';import { useReliableQueue } from '@aplanka/reliable-queue';function RealtimeTaskDashboard() {const [notifications, setNotifications] = useState([]);const { tasks, stats, addTask, queue } = useReliableQueue({queueName: 'dashboard-tasks',processor: async (data) => {await processTask(data);}});// Subscribe to queue events for real-time updatesuseEffect(() => {const unsubscribeCompleted = queue.on('taskCompleted', (task) => {setNotifications(prev => [...prev, {id: Date.now(),type: 'success',message: `Task ${task.id} completed successfully`,timestamp: new Date()}]);});const unsubscribeFailed = queue.on('taskFailed', (task, error) => {setNotifications(prev => [...prev, {id: Date.now(),type: 'error',message: `Task ${task.id} failed: ${error.message}`,timestamp: new Date()}]);});const unsubscribeStarted = queue.on('taskStarted', (task) => {setNotifications(prev => [...prev, {id: Date.now(),type: 'info',message: `Task ${task.id} started processing`,timestamp: new Date()}]);});return () => {unsubscribeCompleted();unsubscribeFailed();unsubscribeStarted();};}, [queue]);// Auto-clear old notificationsuseEffect(() => {const timer = setInterval(() => {setNotifications(prev =>prev.filter(notif => Date.now() - notif.timestamp.getTime() < 10000));}, 1000);return () => clearInterval(timer);}, []);return (<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">{/* Real-time Stats */}<div className="bg-white p-6 rounded-lg shadow"><h3 className="text-lg font-semibold mb-4">Queue Stats</h3><div className="space-y-2"><div className="flex justify-between"><span>Total Tasks:</span><span className="font-bold">{stats.total}</span></div><div className="flex justify-between"><span>Processing:</span><span className="font-bold text-blue-600">{stats.processing}</span></div><div className="flex justify-between"><span>Completed:</span><span className="font-bold text-green-600">{stats.completed}</span></div><div className="flex justify-between"><span>Failed:</span><span className="font-bold text-red-600">{stats.failed}</span></div></div></div>{/* Live Task List */}<div className="bg-white p-6 rounded-lg shadow"><h3 className="text-lg font-semibold mb-4">Active Tasks</h3><div className="max-h-64 overflow-y-auto space-y-2">{tasks.slice(0, 10).map(task => (<div key={task.id} className="p-2 bg-gray-50 rounded text-sm"><div className="flex justify-between items-center"><span className="truncate">{task.id}</span><TaskStatusBadge status={task.status} /></div></div>))}</div></div>{/* Real-time Notifications */}<div className="bg-white p-6 rounded-lg shadow"><h3 className="text-lg font-semibold mb-4">Live Updates</h3><div className="max-h-64 overflow-y-auto space-y-2">{notifications.map(notification => (<NotificationItem key={notification.id} notification={notification} />))}</div></div></div>);}
Live Updates
Components automatically re-render when queue state changes, keeping your UI synchronized.
Event Subscriptions
Subscribe to specific events for custom notifications and UI updates.
Performance
Efficient updates using React's built-in optimization and selective re-rendering.
TypeScript Integration
Reliable Queue provides full TypeScript support with type-safe hooks and interfaces.
Type Safety Features
Generic Hook
// Type-safe task data interface MyTask { userId: string; action: string; } const { addTask, tasks } = useReliableQueue<MyTask>({ processor: async (data: MyTask) => { // data is properly typed } });
Return Types
// All return values are properly typed const { tasks, // QueuedTask<MyTask>[] stats, // QueueStats addTask, // (data: MyTask) => string retryTask, // (id: string) => boolean } = useReliableQueue<MyTask>();
React Integration Best Practices
Hook Usage
- • Use meaningful queue names for shared queues
- • Provide processor functions for automatic processing
- • Enable persistence for critical user data
- • Use TypeScript generics for type safety
Performance Tips
- • Use useMemo and useCallback for expensive operations
- • Unsubscribe from events in useEffect cleanup
- • Consider pagination for large task lists
- • Use React.memo for task item components