or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

async-operations.mddata-structures.mddom-interactions.mdeffects.mdindex.mdperformance.mdspecialized-hooks.mdstate-management.mdstorage.mdtimers.md
tile.json

async-operations.mddocs/

Async Operations

Powerful hooks for managing asynchronous operations including data fetching, request management, function execution control, and advanced async patterns.

Capabilities

Request Management

useRequest

Comprehensive async request management with extensive plugin system for caching, polling, retry, and more.

/**
 * Comprehensive async request management hook
 * @param service - Async function that returns a Promise
 * @param options - Configuration options for request behavior
 * @returns Object with request state and control methods
 */
function useRequest<TData, TParams extends any[]>(
  service: Service<TData, TParams>,
  options?: Options<TData, TParams>
): Result<TData, TParams>;

// Service function type
type Service<TData, TParams extends any[]> = (...args: TParams) => Promise<TData>;

// Core request result interface
interface Result<TData, TParams extends any[]> {
  /** Response data */
  data?: TData;
  /** Loading state */
  loading: boolean;
  /** Error object if request failed */
  error?: Error;
  /** Parameters from last request */
  params?: TParams;
  
  // Control methods
  /** Execute request with optional parameters */
  run: (...params: TParams) => void;
  /** Execute request and return Promise */
  runAsync: (...params: TParams) => Promise<TData>;
  /** Refresh with last parameters */
  refresh: () => void;
  /** Refresh and return Promise */
  refreshAsync: () => Promise<TData>;
  /** Cancel ongoing request */
  cancel: () => void;
  /** Update data without request */
  mutate: (data?: TData | ((oldData?: TData) => TData)) => void;
}

// Configuration options
interface Options<TData, TParams extends any[]> {
  // Basic options
  /** Manual mode - don't auto-execute on mount */
  manual?: boolean;
  /** Default parameters for auto-execution */
  defaultParams?: TParams;
  /** Dependencies that trigger refresh */
  refreshDeps?: DependencyList;
  /** Format result data */
  formatResult?: (res: any) => TData;
  /** Success callback */
  onSuccess?: (data: TData, params: TParams) => void;
  /** Error callback */
  onError?: (e: Error, params: TParams) => void;
  /** Before request callback */
  onBefore?: (params: TParams) => void;
  /** Finally callback */
  onFinally?: (params: TParams, data?: TData, e?: Error) => void;
  
  // Caching
  /** Cache key for request results */
  cacheKey?: string;
  /** Cache time in milliseconds */
  cacheTime?: number;
  /** Stale time in milliseconds */
  staleTime?: number;
  /** Use cache on error */
  setCache?: (data: TData) => void;
  /** Get cache data */
  getCache?: () => TData | undefined;
  
  // Loading delay
  /** Delay before setting loading to true */
  loadingDelay?: number;
  
  // Polling
  /** Polling interval in milliseconds */
  pollingInterval?: number;
  /** Polling when window hidden */
  pollingWhenHidden?: boolean;
  /** Error retry interval */
  pollingErrorRetryCount?: number;
  
  // Refresh on events
  /** Refresh when window refocused */
  refreshOnWindowFocus?: boolean;
  /** Focus timeout before refresh */
  focusTimespan?: number;
  
  // Debounce & Throttle
  /** Debounce wait time */
  debounceWait?: number;
  /** Debounce options */
  debounceLeading?: boolean;
  debounceTrailing?: boolean;
  debounceMaxWait?: number;
  /** Throttle wait time */
  throttleWait?: number;
  throttleLeading?: boolean;
  throttleTrailing?: boolean;
  
  // Retry
  /** Retry count on error */
  retryCount?: number;
  /** Retry interval */
  retryInterval?: number;
  
  // Ready state
  /** Execute only when ready */
  ready?: boolean;
  
  // Pagination helpers (for pagination plugin)
  defaultPageSize?: number;
  
  // SWR options
  /** Revalidate on mount */
  revalidateOnMount?: boolean;
}

Basic Usage Example:

import { useRequest } from 'ahooks';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserProfile({ userId }: { userId: number }) {
  // Basic request
  const { data: user, loading, error } = useRequest(
    () => fetch(`/api/users/${userId}`).then(res => res.json())
  );
  
  // Manual request with parameters
  const { data: posts, loading: postsLoading, run: loadPosts } = useRequest(
    (page: number, pageSize: number) => 
      fetch(`/api/users/${userId}/posts?page=${page}&size=${pageSize}`)
        .then(res => res.json()),
    {
      manual: true,
      onSuccess: (data) => {
        console.log('Posts loaded:', data);
      },
      onError: (error) => {
        console.error('Failed to load posts:', error);
      }
    }
  );
  
  if (loading) return <div>Loading user...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <h1>{user?.name}</h1>
      <p>{user?.email}</p>
      <button onClick={() => loadPosts(1, 10)}>
        Load Posts
      </button>
      {postsLoading && <div>Loading posts...</div>}
    </div>
  );
}

Advanced Features Example:

import { useRequest } from 'ahooks';

function SearchComponent() {
  // Request with debouncing, caching, and polling
  const [query, setQuery] = useState('');
  
  const { 
    data: searchResults, 
    loading, 
    refresh, 
    cancel,
    mutate 
  } = useRequest(
    (searchTerm: string) => 
      fetch(`/api/search?q=${searchTerm}`).then(res => res.json()),
    {
      // Cache results for 5 minutes
      cacheKey: `search-${query}`,
      cacheTime: 5 * 60 * 1000,
      
      // Debounce search requests
      debounceWait: 300,
      debounceLeading: false,
      debounceTrailing: true,
      
      // Refresh when window regains focus
      refreshOnWindowFocus: true,
      
      // Polling for real-time updates
      pollingInterval: 30000, // Poll every 30 seconds
      pollingWhenHidden: false,
      
      // Retry on error
      retryCount: 3,
      retryInterval: 1000,
      
      // Only search when query is not empty
      ready: !!query,
      
      // Default params
      defaultParams: [query],
      refreshDeps: [query],
      
      formatResult: (data) => data.results || [],
      
      onSuccess: (data, params) => {
        console.log(`Search for "${params[0]}" returned ${data.length} results`);
      },
      
      onError: (error, params) => {
        console.error(`Search failed for "${params[0]}":`, error);
      }
    }
  );
  
  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
      />
      <button onClick={() => refresh()}>Refresh</button>
      <button onClick={cancel}>Cancel</button>
      <button onClick={() => mutate([])}>Clear Results</button>
      
      {loading && <div>Searching...</div>}
      
      <ul>
        {searchResults?.map(item => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
}

Cache Management:

import { useRequest, clearCache } from 'ahooks';

function CachedDataComponent() {
  const { data, loading, mutate } = useRequest(
    () => fetch('/api/expensive-data').then(res => res.json()),
    {
      cacheKey: 'expensive-data',
      cacheTime: 10 * 60 * 1000, // Cache for 10 minutes
      staleTime: 5 * 60 * 1000,   // Consider stale after 5 minutes
      
      // Custom cache management
      setCache: (data) => {
        localStorage.setItem('expensive-data-cache', JSON.stringify({
          data,
          timestamp: Date.now()
        }));
      },
      
      getCache: () => {
        const cached = localStorage.getItem('expensive-data-cache');
        if (cached) {
          const { data, timestamp } = JSON.parse(cached);
          // Return cache if less than 10 minutes old
          if (Date.now() - timestamp < 10 * 60 * 1000) {
            return data;
          }
        }
        return undefined;
      }
    }
  );
  
  const clearAllCache = () => {
    clearCache(); // Clear all ahooks request cache
    localStorage.removeItem('expensive-data-cache');
    mutate(undefined); // Clear local state
  };
  
  return (
    <div>
      <button onClick={clearAllCache}>Clear Cache</button>
      {loading ? 'Loading...' : JSON.stringify(data)}
    </div>
  );
}

Function Execution Control

useLockFn

Prevents concurrent execution of async functions, ensuring only one execution at a time.

/**
 * Prevents concurrent execution of async function
 * @param fn - Async function to lock
 * @returns Locked version of function that prevents concurrent calls
 */
function useLockFn<P extends any[] = any[], V = any>(
  fn: (...args: P) => Promise<V>
): (...args: P) => Promise<V | undefined>;

Usage Example:

import { useLockFn } from 'ahooks';
import { useState } from 'react';

function PaymentButton() {
  const [status, setStatus] = useState('idle');
  
  // Prevent double submission of payment
  const processPayment = useLockFn(async (amount: number) => {
    setStatus('processing');
    
    try {
      // Simulate payment processing
      await new Promise(resolve => setTimeout(resolve, 2000));
      
      const response = await fetch('/api/payment', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount })
      });
      
      if (!response.ok) throw new Error('Payment failed');
      
      const result = await response.json();
      setStatus('success');
      return result;
    } catch (error) {
      setStatus('error');
      throw error;
    }
  });
  
  return (
    <div>
      <button 
        onClick={() => processPayment(99.99)}
        disabled={status === 'processing'}
      >
        {status === 'processing' ? 'Processing...' : 'Pay $99.99'}
      </button>
      <p>Status: {status}</p>
    </div>
  );
}

Debounce and Throttle

useDebounce

Debounces a value, useful for search inputs and API calls.

/**
 * Debounces a value to limit update frequency
 * @param value - Value to debounce
 * @param options - Debounce configuration options
 * @returns Debounced value
 */
function useDebounce<T>(value: T, options?: DebounceOptions): T;

interface DebounceOptions {
  /** Debounce delay in milliseconds (default: 1000) */
  wait?: number;
  /** Execute on leading edge (default: false) */
  leading?: boolean;
  /** Execute on trailing edge (default: true) */
  trailing?: boolean;
  /** Maximum delay before execution (default: undefined) */
  maxWait?: number;
}

useDebounceFn

Debounces a function execution with control methods.

/**
 * Debounces function execution with control methods
 * @param fn - Function to debounce
 * @param options - Debounce configuration options
 * @returns Object with debounced function and control methods
 */
function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions): {
  /** Debounced function */
  run: T;
  /** Cancel pending execution */
  cancel: () => void;
  /** Execute immediately and cancel pending */
  flush: () => void;
};

type noop = (...args: any[]) => any;

useThrottle

Throttles a value to limit update frequency.

/**
 * Throttles a value to limit update frequency
 * @param value - Value to throttle
 * @param options - Throttle configuration options
 * @returns Throttled value
 */
function useThrottle<T>(value: T, options?: ThrottleOptions): T;

interface ThrottleOptions {
  /** Throttle interval in milliseconds (default: 1000) */
  wait?: number;
  /** Execute on leading edge (default: true) */
  leading?: boolean;
  /** Execute on trailing edge (default: false) */
  trailing?: boolean;
}

useThrottleFn

Throttles function execution with control methods.

/**
 * Throttles function execution with control methods
 * @param fn - Function to throttle
 * @param options - Throttle configuration options
 * @returns Object with throttled function and control methods
 */
function useThrottleFn<T extends noop>(fn: T, options?: ThrottleOptions): {
  /** Throttled function */
  run: T;
  /** Cancel pending execution */
  cancel: () => void;
  /** Execute immediately and cancel pending */
  flush: () => void;
};

Debounce/Throttle Usage Example:

import { useDebounce, useDebounceFn, useThrottle, useThrottleFn } from 'ahooks';
import { useState } from 'react';

function SearchAndScroll() {
  const [searchTerm, setSearchTerm] = useState('');
  const [scrollPosition, setScrollPosition] = useState(0);
  
  // Debounce search term for API calls
  const debouncedSearchTerm = useDebounce(searchTerm, { wait: 500 });
  
  // Debounce search function
  const { run: debouncedSearch, cancel: cancelSearch } = useDebounceFn(
    (term: string) => {
      console.log('Searching for:', term);
      // Make API call here
    },
    { wait: 300, trailing: true }
  );
  
  // Throttle scroll position updates
  const throttledScrollPosition = useThrottle(scrollPosition, { wait: 100 });
  
  // Throttle scroll handler
  const { run: throttledScrollHandler } = useThrottleFn(
    () => {
      console.log('Scroll position updated:', window.scrollY);
      setScrollPosition(window.scrollY);
    },
    { wait: 50, leading: true, trailing: false }
  );
  
  // Use debounced search term for API calls
  useEffect(() => {
    if (debouncedSearchTerm) {
      // API call with debounced term
      fetch(`/api/search?q=${debouncedSearchTerm}`);
    }
  }, [debouncedSearchTerm]);
  
  // Use throttled scroll handler
  useEffect(() => {
    window.addEventListener('scroll', throttledScrollHandler);
    return () => window.removeEventListener('scroll', throttledScrollHandler);
  }, [throttledScrollHandler]);
  
  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => {
          setSearchTerm(e.target.value);
          debouncedSearch(e.target.value);
        }}
        placeholder="Search..."
      />
      <button onClick={cancelSearch}>Cancel Search</button>
      
      <p>Current search: {searchTerm}</p>
      <p>Debounced search: {debouncedSearchTerm}</p>
      <p>Throttled scroll: {throttledScrollPosition}</p>
    </div>
  );
}

Cache Management Utilities

clearCache

Utility function to clear useRequest cache entries.

/**
 * Clears useRequest cache entries
 * @param cacheKey - Specific cache key(s) to clear, or clear all if undefined
 */
function clearCache(cacheKey?: string | string[]): void;

Usage Example:

import { useRequest, clearCache } from 'ahooks';

function CacheManagement() {
  const { data: userData } = useRequest(
    () => fetch('/api/user').then(res => res.json()),
    { cacheKey: 'user-data' }
  );
  
  const { data: postsData } = useRequest(
    () => fetch('/api/posts').then(res => res.json()),
    { cacheKey: 'posts-data' }
  );
  
  const clearUserCache = () => clearCache('user-data');
  const clearPostsCache = () => clearCache('posts-data');
  const clearMultiple = () => clearCache(['user-data', 'posts-data']);
  const clearAllCache = () => clearCache();
  
  return (
    <div>
      <button onClick={clearUserCache}>Clear User Cache</button>
      <button onClick={clearPostsCache}>Clear Posts Cache</button>
      <button onClick={clearMultiple}>Clear Multiple</button>
      <button onClick={clearAllCache}>Clear All Cache</button>
    </div>
  );
}

Common Types

// React dependency list
type DependencyList = ReadonlyArray<any>;

// Generic function type
type noop = (...args: any[]) => any;

// Promise-based service function
type Service<TData, TParams extends any[]> = (...args: TParams) => Promise<TData>;