8
Hooks

usePolling

A hook for performing interval-based API polling with control functions and error handling.

usePolling

The usePolling hook provides a way to fetch data from an API endpoint at regular intervals. It includes comprehensive controls for starting, pausing, and stopping polling, along with error handling, retry limits, and state management.

Cryptocurrency Tracker
Paused
TrackAssetPrice (USD)24h Change
B
Bitcoin
BTC
$56789.34+2.45%
E
Ethereum
ETH
$2834.56-1.2%
S
Solana
SOL
$123.78+5.67%
D
Polkadot
DOT
$34.56+0.89%
D
Dogecoin
DOGE
$0.1234-3.45%

Toggle currencies to track specific assets

Data refreshes every 5 seconds

Installation

Install the usePolling hook using:

npx axionjs-ui add hook use-polling

File Structure

use-polling.ts

Parameters

PropTypeDefault
options
PollingOptions<T>
Required

Return Value

PropTypeDefault
data
T | null
-
error
Error | null
-
status
'IDLE' | 'POLLING' | 'PAUSED' | 'ERROR' | 'STOPPED'
-
lastUpdated
number | null
-
errorCount
number
-
isPolling
boolean
-
isPaused
boolean
-
isStopped
boolean
-
isError
boolean
-
startPolling
() => void
-
pausePolling
() => void
-
stopPolling
() => void
-
resetErrors
() => void
-
fetchNow
() => void
-

Examples

Server Status Monitoring

A real-time dashboard for monitoring server health metrics with automatic polling.

Server Monitoring Dashboard
Monitoring Paused

Web Server 01

web | us-east

Fetching server status...

API Server 01

api | us-east

Fetching server status...

Database 01

database | us-east

Fetching server status...

Cache Server 01

cache | us-west

Fetching server status...

Worker 01

worker | eu-central

Fetching server status...

Monitoring 5 servers across 3 regions

Refreshes every 10 seconds

Real-time Stock Market Data

import { useEffect, useState } from "react";
import { usePolling } from "@/hooks/use-polling";
 
interface StockData {
  symbol: string;
  price: number;
  change: number;
  percentChange: number;
  volume: number;
  lastUpdated: string;
}
 
function StockTickerWidget() {
  // Stock symbols to track
  const [symbols, setSymbols] = useState(['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA']);
  const [stockData, setStockData] = useState<Record<string, StockData>>({});
  const [marketStatus, setMarketStatus] = useState<'open' | 'closed' | 'pre-market' | 'after-hours'>('closed');
  
  // Check if market is currently open
  const isMarketOpen = marketStatus === 'open' || marketStatus === 'pre-market' || marketStatus === 'after-hours';
  
  // Determine polling interval based on market status
  const pollingInterval = marketStatus === 'open' ? 5000 : // 5 seconds when market is open
                          marketStatus === 'pre-market' || marketStatus === 'after-hours' ? 15000 : // 15 seconds for extended hours
                          60000; // 1 minute when market is closed
  
  // Fetch stock data from API
  const fetchStockData = async () => {
    try {
      // Fetch market status first
      const statusResponse = await fetch('/api/market/status');
      if (!statusResponse.ok) throw new Error('Failed to fetch market status');
      const { status } = await statusResponse.json();
      setMarketStatus(status);
      
      // Fetch price data for all symbols
      const symbolsParam = symbols.join(',');
      const response = await fetch(`/api/stocks/quotes?symbols=${symbolsParam}`);
      if (!response.ok) throw new Error('Failed to fetch stock data');
      
      const data = await response.json();
      setStockData(data);
      return data;
    } catch (error) {
      console.error('Error fetching stock data:', error);
      throw error;
    }
  };
  
  // Use polling hook with dynamic interval
  const { 
    isPolling, 
    startPolling, 
    pausePolling, 
    error,
    resetErrors
  } = usePolling({
    fetchFn: fetchStockData,
    interval: pollingInterval,
    autoStart: true,
    maxErrors: 3,
  });
  
  // Update polling interval when market status changes
  useEffect(() => {
    if (isPolling) {
      pausePolling();
      startPolling();
    }
  }, [pollingInterval]);
  
  return (
    <div className="stock-ticker">
      <header>
        <h2>Stock Ticker</h2>
        <div className="market-status">
          Market: <span className={marketStatus}>{marketStatus}</span>
          <button onClick={isPolling ? pausePolling : startPolling}>
            {isPolling ? "Pause" : "Resume"}
          </button>
        </div>
      </header>
      
      {error && (
        <div className="error-banner">
          <p>{error.message}</p>
          <button onClick={resetErrors}>Try Again</button>
        </div>
      )}
      
      <table className="stock-table">
        <thead>
          <tr>
            <th>Symbol</th>
            <th>Price</th>
            <th>Change</th>
            <th>Volume</th>
          </tr>
        </thead>
        <tbody>
          {symbols.map(symbol => {
            const stock = stockData[symbol];
            return (
              <tr key={symbol}>
                <td>{symbol}</td>
                <td>{stock ? `${stock.price.toFixed(2)}` : '--'}</td>
                <td className={stock?.change > 0 ? 'positive' : stock?.change < 0 ? 'negative' : ''}>
                  {stock ? (
                    <>
                      {stock.change > 0 ? '+' : ''}{stock.change.toFixed(2)} 
                      ({stock.percentChange.toFixed(2)}%)
                    </>
                  ) : '--'}
                </td>
                <td>{stock?.volume.toLocaleString() || '--'}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
      
      <footer>
        <small>Data updates {isMarketOpen 
          ? `every ${pollingInterval / 1000} seconds` 
          : 'every minute'
        }</small>
      </footer>
    </div>
  );
}

Auto-Saving Form

A form with automatic saving functionality that periodically sends updates to the server.

import { useState, useEffect } from "react";
import { usePolling } from "@/hooks/use-polling";
 
// Types for our form data
interface DocumentData {
  title: string;
  content: string;
  lastSaved?: Date;
  isDirty: boolean;
}
 
function AutoSaveEditor() {
  // Initial document state
  const [document, setDocument] = useState<DocumentData>({
    title: "Untitled Document",
    content: "",
    isDirty: false
  });
  
  // Save status indicators
  const [saveStatus, setSaveStatus] = useState<'saved' | 'saving' | 'error'>('saved');
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  
  // Function to save the document to the server
  const saveDocument = async () => {
    // Skip saving if nothing has changed
    if (!document.isDirty) {
      return document;
    }
    
    try {
      setSaveStatus('saving');
      
      // Simulate a network request
      const response = await fetch('/api/documents/save', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          title: document.title,
          content: document.content,
        }),
      });
      
      if (!response.ok) {
        throw new Error(`Failed to save: ${response.statusText}`);
      }
      
      // Update document with saved status
      const savedDocument = {
        ...document,
        lastSaved: new Date(),
        isDirty: false,
      };
      
      setDocument(savedDocument);
      setSaveStatus('saved');
      setErrorMessage(null);
      
      return savedDocument;
    } catch (error) {
      setSaveStatus('error');
      setErrorMessage(error.message || 'Failed to save document');
      throw error;
    }
  };
  
  // Use polling to auto-save the document every 30 seconds
  const { 
    isPolling,
    error,
    lastUpdated,
    pausePolling,
    startPolling,
    fetchNow,
  } = usePolling({
    fetchFn: saveDocument,
    interval: 30000, // 30 seconds
    autoStart: true,
    maxErrors: 5,
  });
  
  // Handle form input changes
  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    const { name, value } = e.target;
    
    setDocument(prev => ({
      ...prev,
      [name]: value,
      isDirty: true, // Mark document as changed
    }));
  };
  
  // Save manually
  const handleManualSave = () => {
    fetchNow();
  };
  
  return (
    <div className="auto-save-editor">
      <div className="editor-header">
        <input
          type="text"
          name="title"
          value={document.title}
          onChange={handleInputChange}
          className="title-input"
          placeholder="Document Title"
        />
        
        <div className="editor-controls">
          <div className="save-status">
            {saveStatus === 'saving' && <span>Saving...</span>}
            {saveStatus === 'saved' && document.lastSaved && (
              <span>Saved at {document.lastSaved.toLocaleTimeString()}</span>
            )}
            {saveStatus === 'error' && (
              <span className="error">Save failed!</span>
            )}
          </div>
          
          <div className="action-buttons">
            <button
              onClick={isPolling ? pausePolling : startPolling}
              title={isPolling ? "Disable auto-save" : "Enable auto-save"}
            >
              {isPolling ? "Auto-save on" : "Auto-save off"}
            </button>
            
            <button
              onClick={handleManualSave}
              disabled={!document.isDirty || saveStatus === 'saving'}
            >
              Save now
            </button>
          </div>
        </div>
      </div>
      
      {errorMessage && (
        <div className="error-message">
          {errorMessage}
        </div>
      )}
      
      <textarea
        name="content"
        value={document.content}
        onChange={handleInputChange}
        className="content-editor"
        placeholder="Start typing your document..."
        rows={20}
      />
      
      <div className="editor-footer">
        <span>
          {isPolling 
            ? `Auto-saving every ${30} seconds` 
            : "Auto-save is turned off"
          }
        </span>
        
        {document.isDirty && (
          <span className="unsaved-indicator">You have unsaved changes</span>
        )}
      </div>
    </div>
  );
}

Use Cases

  • Data Dashboards: Keep dashboards updated with the latest metrics and KPIs
  • Financial Applications: Track stock prices, currency exchange rates, or cryptocurrency values
  • Monitoring Systems: Poll server status, system health, or application metrics
  • Chat Applications: Check for new messages when WebSockets aren’t available
  • Form Auto-Save: Automatically save form data at regular intervals
  • Content Updates: Refresh content feeds, news, or notifications
  • IoT Applications: Poll devices for status updates or sensor data
  • Sports/Gaming: Update live scores, match status, or game statistics
  • Collaborative Editing: Poll for document changes from other users

Performance Considerations

  • Battery Impact: Frequent polling can impact battery life on mobile devices
  • Network Usage: Be mindful of data usage, especially on metered connections
  • Server Load: Avoid polling too frequently to prevent overwhelming your server
  • Variable Intervals: Consider using longer intervals when the application is not actively used
  • Conditional Polling: Only poll when necessary (e.g., stop polling when a tab is not visible)
  • Backoff Strategy: Implement exponential backoff for error retry attempts

Accessibility

When implementing polling in user interfaces:

  • Use ARIA live regions to announce important updates to screen readers
  • Provide visual indicators when data is being refreshed
  • Allow users to pause automatic updates if needed
  • Maintain focus position when content updates

Best Practices

  • Debounce User Actions: Avoid triggering multiple fetches when users interact with controls
  • Error Handling: Always handle errors gracefully with clear user feedback
  • Loading States: Show loading indicators during long-running fetches
  • Last Updated Time: Display when data was last updated for user awareness
  • Cleanup: Always clean up timeouts when components unmount
  • User Control: Give users control to pause, resume, or adjust polling frequency
  • Batch Updates: For multiple polled resources, consider batching them to reduce requests

On this page