Skip to main content

Overview

The useDownload hook provides a complete solution for downloading podcast audio files with real-time progress tracking, retry logic, and cancellation support. It includes built-in UI feedback through toast notifications and handles network errors gracefully.

Hook Signature

const { 
  isLoading, 
  progress, 
  isCancelled, 
  handleDownload, 
  cancelDownload 
} = useDownload();

Return Values

isLoading
boolean
Indicates whether a download is currently in progress
progress
number
Download progress percentage (0-100)
isCancelled
boolean
Indicates whether the current download has been cancelled
handleDownload
function
Function to initiate a downloadParameters:
  • audioUrl (string): The URL of the audio file to download
  • fileName (string): The name to save the file as
cancelDownload
function
Function to cancel an ongoing download. No parameters required.

Features

Progress Tracking

The hook tracks download progress in real-time and updates the UI with the current percentage:
const currentProgress = Math.round((loaded / total) * 100);
setProgress(currentProgress);

Retry Logic

Automatic retry with exponential backoff (up to 3 attempts):
const MAX_RETRIES = 3;

if (attempt < MAX_RETRIES) {
    await new Promise((resolve) => setTimeout(resolve, 1000 * (attempt + 1)));
    return fetchWithRetry(url, fileName, attempt + 1);
}

Cancellation Support

Downloads can be cancelled at any time using the AbortController API:
const cancelDownload = () => {
    if (abortController.current) {
        abortController.current.abort();
        setIsCancelled(true);
        // Show cancellation toast
    }
};

Toast Notifications

Built-in toast notifications for:
  • Download progress updates
  • Download completion
  • Download cancellation
  • Download errors

Usage Examples

Basic Usage

From /home/daytona/workspace/source/src/components/PodcastDetail/PodcastDetail.jsx:52:
import useDownload from "../../hooks/useDownload";

const PodcastDetail = ({ onPlayPodcast }) => {
    const { isLoading, progress, isCancelled, handleDownload, cancelDownload } = useDownload();
    const isMobile = useMobileDetect();

    // Use handleDownload when user clicks download button
    const onDownloadClick = () => {
        handleDownload(podcast.audioUrl, `${podcast.title}.mp3`);
    };

    return (
        <button onClick={onDownloadClick} disabled={isLoading}>
            {isLoading ? `Downloading ${progress}%` : 'Download'}
        </button>
    );
};

MP3 Player Integration

From /home/daytona/workspace/source/src/components/MP3Player/MP3Player.jsx:52:
import useDownload from "../../hooks/useDownload";

const MP3Player = () => {
    const { isLoading, handleDownload, progress } = useDownload();
    
    // Download button in player controls
};

Last Podcast Component

From /home/daytona/workspace/source/src/components/LastPodcast/LastPodcast.jsx:48:
import useDownload from "../../hooks/useDownload";

const LastPodcast = () => {
    const { isLoading, progress, isCancelled, handleDownload, cancelDownload } = useDownload();
    
    // Featured podcast download functionality
};

Implementation Details

State Management

The hook uses multiple useState and useRef hooks to manage download state:
const [isLoading, setIsLoading] = useState(false);
const [progress, setProgress] = useState(0);
const [isCancelled, setIsCancelled] = useState(false);
const abortController = useRef(null);
const toastIdRef = useRef(null);
const retryCount = useRef(0);

Download Process

  1. Create AbortController for cancellation support
  2. Fetch audio file with retry logic
  3. Read response as stream
  4. Track progress and update UI
  5. Create blob from chunks
  6. Trigger browser download
  7. Clean up resources

Error Handling

The hook handles multiple error scenarios:
  • Network errors (with retry)
  • HTTP errors
  • User cancellation
  • Timeout errors

Best Practices

Always disable download buttons when isLoading is true to prevent multiple simultaneous downloads.
The hook uses browser download functionality which may be blocked by popup blockers if not triggered by a direct user action.

Browser Compatibility

This hook uses:
  • Fetch API with AbortController
  • Streams API (ReadableStream)
  • Blob API
  • URL.createObjectURL
All modern browsers are supported. For older browsers, consider adding polyfills.

Build docs developers (and LLMs) love