Back to Documentation

React Integration

Complete guide to using Reliable Queue with React applications, including hooks, context, and real-time updates.

Basic Usage

The useReliableQueue hook provides a simple way to integrate queue functionality into your React components.

Basic useReliableQueue Hook
tsx
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 logic
console.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>
<button
onClick={() => 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' && (
<button
onClick={() => 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)

string

Default: undefined

processor?

Function to process tasks

TaskProcessor<T>

Default: undefined

useSingleton?

Use singleton QueueManager for named queues

boolean

Default: true

maxRetries?

Maximum retry attempts

number

Default: 3

retryDelay?

Base retry delay in milliseconds

number

Default: 1000

exponentialBackoff?

Use exponential backoff for retries

boolean

Default: true

concurrency?

Maximum concurrent tasks

number

Default: 1

persistent?

Persist queue to localStorage

boolean

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.

Advanced Email Queue Manager
tsx
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 sending
const 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 operations
retryAllTasks();
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">
<input
type="email"
placeholder="To"
value={emailData.to}
onChange={(e) => setEmailData(prev => ({ ...prev, to: e.target.value }))}
className="w-full p-2 border rounded"
/>
<input
type="text"
placeholder="Subject"
value={emailData.subject}
onChange={(e) => setEmailData(prev => ({ ...prev, subject: e.target.value }))}
className="w-full p-2 border rounded"
/>
<textarea
placeholder="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.

Queue Context Provider
tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { ReliableQueue, QueueManager } from '@aplanka/reliable-queue';
// Create Queue Context
const QueueContext = createContext();
// Queue Provider Component
export 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 context
export 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 Provider
function App() {
return (
<QueueProvider>
<EmailManager />
<ImageProcessor />
<NotificationCenter />
</QueueProvider>
);
}
// Components using shared queues
function 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.

Custom File Upload Hook
tsx
import { useReliableQueue } from '@aplanka/reliable-queue';
import { useCallback, useMemo } from 'react';
// Custom hook for file uploads
export 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 data
tasks,
stats,
retryTask,
// Custom upload functions
uploadFile,
uploadFiles,
cancelUpload,
// Computed values
uploadProgress,
activeUploads,
// Status helpers
isUploading: activeUploads.length > 0,
hasFailedUploads: stats.failed > 0
};
}
// Usage in component
function 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">
<div
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
style={{ width: `${uploadProgress}%` }}
/>
</div>
</div>
<input
type="file"
multiple
onChange={(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 => (
<FileUploadItem
key={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.

Real-time Task Dashboard
tsx
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 updates
useEffect(() => {
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 notifications
useEffect(() => {
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