or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced.mdcore-request.mdindex.mdload-more.mdpagination.md
tile.json

advanced.mddocs/

Advanced Configuration

Global configuration, context providers, advanced options, and specialized hooks for complex application requirements and enterprise-level data management patterns.

Capabilities

UseRequestProvider

Global context provider for configuring default options across all useRequest hooks in your application.

/**
 * Global configuration provider for useRequest hooks
 * @param props - Provider props with configuration value
 * @returns Provider component for React context
 */
const UseRequestProvider: React.ComponentType<{
  value: Config;
  children: React.ReactNode;
}>;

/**
 * Configuration type for global provider
 */
type Config = Options<any, any, any, any> | BasePaginatedOptions<any> | LoadMoreOptions<any>;

UseAPIProvider (Deprecated)

Legacy provider alias maintained for backward compatibility with umijs plugin-request.

/**
 * @deprecated Use UseRequestProvider instead
 * Legacy provider alias for backward compatibility
 */
const UseAPIProvider: React.ComponentType<{
  value: Config;
  children: React.ReactNode;
}>;

Usage Examples:

import { UseRequestProvider } from "@ahooksjs/use-request";
import axios from "axios";

// Global configuration at app level
const App = () => {
  const globalConfig = {
    // Custom request method for all hooks
    requestMethod: (service: any) => {
      if (typeof service === 'string') {
        return axios.get(service).then(res => res.data);
      }
      return axios(service).then(res => res.data);
    },
    
    // Global error handling
    onError: (error: Error) => {
      console.error('Global request error:', error);
      if (error.message.includes('401')) {
        // Handle unauthorized globally
        redirectToLogin();
      }
    },
    
    // Global success logging
    onSuccess: (data: any, params: any) => {
      console.log('Request succeeded:', { data, params });
    },
    
    // Default caching
    cacheTime: 5 * 60 * 1000, // 5 minutes
    
    // Default focus refresh
    refreshOnWindowFocus: true,
    focusTimespan: 5000,
    
    // Default error behavior
    throwOnError: false
  };

  return (
    <UseRequestProvider value={globalConfig}>
      <Router>
        <Routes>
          <Route path="/" component={HomePage} />
          <Route path="/users" component={UsersPage} />
        </Routes>
      </Router>
    </UseRequestProvider>
  );
};

// Child components inherit global config
const UserProfile = ({ userId }: { userId: string }) => {
  // This hook inherits global requestMethod, onError, etc.
  const { data, loading, error } = useRequest(`/api/user/${userId}`);
  
  return (
    <div>
      {loading && <div>Loading...</div>}
      {error && <div>Error: {error.message}</div>}
      {data && <div>Welcome, {data.name}!</div>}
    </div>
  );
};

Direct Hook Access

Direct access to specialized hooks for advanced use cases and fine-grained control.

/**
 * Direct access to core async functionality
 */
function useAsync<R, P extends any[]>(
  service: Service<R, P>,
  options?: BaseOptions<R, P>
): BaseResult<R, P>;

/**
 * Direct access to pagination functionality  
 */
function usePaginated<R, Item, U extends Item = any>(
  service: (...p: PaginatedParams) => Promise<R>,
  options: PaginatedOptionsWithFormat<R, Item, U>
): PaginatedResult<Item>;

/**
 * Direct access to load more functionality
 */
function useLoadMore<R extends LoadMoreFormatReturn, RR>(
  service: (...p: LoadMoreParams<R>) => Promise<RR>,
  options: LoadMoreOptionsWithFormat<R, RR>
): LoadMoreResult<R>;

Usage Examples:

import { useAsync, usePaginated, useLoadMore } from "@ahooksjs/use-request";

// Direct useAsync for custom service functions
const customDataHook = useAsync(
  async (filters: SearchFilters) => {
    const results = await Promise.all([
      fetch('/api/users'),
      fetch('/api/posts'),
      fetch('/api/comments')
    ]);
    
    const [users, posts, comments] = await Promise.all(
      results.map(r => r.json())
    );
    
    return { users, posts, comments };
  },
  { manual: true }
);

// Direct usePaginated for custom pagination logic
const customPagination = usePaginated(
  async ({ current, pageSize, sorter, filters }) => {
    // Custom backend integration
    const response = await customApiClient.list({
      page: current,
      limit: pageSize,
      sort: sorter ? `${sorter.field}:${sorter.order}` : undefined,
      where: filters
    });
    
    return {
      total: response.count,
      list: response.items
    };
  },
  {
    paginated: true,
    defaultPageSize: 25
  }
);

// Direct useLoadMore for specialized infinite scroll
const customLoadMore = useLoadMore(
  async (prevData, searchQuery: string) => {
    const cursor = prevData?.nextCursor || null;
    const response = await elasticsearchClient.search({
      query: searchQuery,
      size: 20,
      search_after: cursor
    });
    
    return {
      list: response.hits.hits.map(hit => hit._source),
      nextCursor: response.hits.hits[response.hits.hits.length - 1]?.sort
    };
  },
  {
    loadMore: true,
    isNoMore: (data) => !data?.nextCursor
  }
);

Advanced Configuration Patterns

Environment-Specific Configuration

// Different configs for different environments
const createRequestConfig = (environment: 'development' | 'production' | 'testing') => {
  const baseConfig = {
    onError: (error: Error) => {
      // Always log errors
      console.error('Request failed:', error);
    }
  };

  switch (environment) {
    case 'development':
      return {
        ...baseConfig,
        // Detailed logging in development
        onSuccess: (data: any, params: any) => {
          console.log('✅ Request succeeded:', { data, params });
        },
        // Longer cache times for development
        cacheTime: 10 * 60 * 1000,
        // Always refresh on focus during development
        refreshOnWindowFocus: true
      };
      
    case 'production':
      return {
        ...baseConfig,
        // Minimal logging in production
        onSuccess: () => {
          // Silent success in production
        },
        // Shorter cache times for fresh data
        cacheTime: 2 * 60 * 1000,
        // Less aggressive refresh in production
        refreshOnWindowFocus: false
      };
      
    case 'testing':
      return {
        ...baseConfig,
        // No side effects during testing
        onSuccess: () => {},
        onError: () => {},
        // No caching during tests
        cacheTime: 0,
        refreshOnWindowFocus: false
      };
  }
};

// Usage
const config = createRequestConfig(process.env.NODE_ENV as any);

<UseRequestProvider value={config}>
  <App />
</UseRequestProvider>

Authentication Integration

// Advanced authentication integration
const createAuthenticatedConfig = (getToken: () => string | null) => ({
  requestMethod: async (service: any) => {
    const token = getToken();
    
    if (!token) {
      throw new Error('No authentication token available');
    }
    
    const headers = {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    };
    
    if (typeof service === 'string') {
      const response = await fetch(service, { headers });
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      return response.json();
    }
    
    const response = await fetch(service.url, {
      ...service,
      headers: { ...headers, ...service.headers }
    });
    
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
    
    return response.json();
  },
  
  onError: (error: Error, params: any) => {
    if (error.message.includes('401')) {
      // Token expired or invalid
      localStorage.removeItem('auth-token');
      window.location.href = '/login';
    } else if (error.message.includes('403')) {
      // Insufficient permissions
      showNotification('You do not have permission to access this resource');
    } else {
      // Other errors
      showNotification(`Request failed: ${error.message}`);
    }
  }
});

// Usage with auth context
const AuthenticatedApp = () => {
  const { token } = useAuth();
  const config = createAuthenticatedConfig(() => token);
  
  return (
    <UseRequestProvider value={config}>
      <AppContent />
    </UseRequestProvider>
  );
};

Multiple Provider Contexts

// Different configurations for different parts of app
const APIProviders = ({ children }: { children: React.ReactNode }) => {
  const adminConfig = {
    requestMethod: createAdminApiClient(),
    onError: handleAdminErrors,
    cacheTime: 30 * 1000 // Short cache for admin data
  };
  
  const publicConfig = {
    requestMethod: createPublicApiClient(),
    onError: handlePublicErrors,
    cacheTime: 5 * 60 * 1000 // Longer cache for public data
  };
  
  return (
    <UseRequestProvider value={publicConfig}>
      <PublicRoutes />
      <UseRequestProvider value={adminConfig}>
        <AdminRoutes />
      </UseRequestProvider>
    </UseRequestProvider>
  );
};

// Hooks inherit the nearest provider config
const AdminUsersList = () => {
  // Uses adminConfig
  const { data, loading } = useRequest('/admin/users');
  return <div>{/* Admin UI */}</div>;
};

const PublicBlogPost = () => {
  // Uses publicConfig
  const { data, loading } = useRequest('/api/blog/posts');
  return <div>{/* Public UI */}</div>;
};

Advanced Hook Patterns

Custom Hook Composition

// Compose useRequest with custom logic
const useAuthenticatedRequest = <T = any>(url: string, options?: BaseOptions<T, any[]>) => {
  const { user, token } = useAuth();
  
  return useRequest<T>(
    url,
    {
      ...options,
      ready: !!token && !!user, // Only execute when authenticated
      requestMethod: (service) => {
        const headers = { 'Authorization': `Bearer ${token}` };
        if (typeof service === 'string') {
          return fetch(service, { headers }).then(r => r.json());
        }
        return fetch(service.url, { ...service, headers }).then(r => r.json());
      },
      onError: (error) => {
        if (error.message.includes('401')) {
          logout(); // Custom logout logic
        }
        options?.onError?.(error, [] as any);
      }
    }
  );
};

// Resource-specific hooks
const useUserData = (userId?: string) => {
  return useAuthenticatedRequest(
    userId ? `/api/user/${userId}` : null,
    {
      cacheKey: userId ? `user-${userId}` : undefined,
      ready: !!userId
    }
  );
};

const useInfiniteComments = (postId: string) => {
  return useRequest(
    (prevData) => {
      const cursor = prevData?.nextCursor || null;
      return `/api/posts/${postId}/comments?cursor=${cursor}&limit=20`;
    },
    {
      loadMore: true,
      ready: !!postId,
      isNoMore: (data) => !data?.hasMore,
      formatResult: (response) => ({
        list: response.comments,
        nextCursor: response.nextCursor,
        hasMore: response.hasMore
      })
    }
  );
};

Data Synchronization Patterns

// Synchronize related data across multiple hooks
const useRelatedData = (userId: string) => {
  // User profile
  const userQuery = useRequest(`/api/user/${userId}`, {
    cacheKey: `user-${userId}`,
    staleTime: 5 * 60 * 1000
  });
  
  // User posts
  const postsQuery = useRequest(
    () => `/api/user/${userId}/posts`,
    {
      ready: !!userQuery.data,
      refreshDeps: [userQuery.data?.id],
      cacheKey: `user-posts-${userId}`
    }
  );
  
  // User followers
  const followersQuery = useRequest(
    () => `/api/user/${userId}/followers`,
    {
      ready: !!userQuery.data,
      refreshDeps: [userQuery.data?.id],
      cacheKey: `user-followers-${userId}`
    }
  );
  
  // Aggregate loading and error states
  const loading = userQuery.loading || postsQuery.loading || followersQuery.loading;
  const error = userQuery.error || postsQuery.error || followersQuery.error;
  
  // Refresh all related data
  const refreshAll = useCallback(() => {
    userQuery.refresh();
    postsQuery.refresh();
    followersQuery.refresh();
  }, [userQuery.refresh, postsQuery.refresh, followersQuery.refresh]);
  
  return {
    user: userQuery.data,
    posts: postsQuery.data,
    followers: followersQuery.data,
    loading,
    error,
    refreshAll
  };
};

Advanced Caching Strategies

// Multi-level caching with background refresh
const useSmartCache = <T>(
  key: string,
  fetcher: () => Promise<T>,
  options: {
    cacheTime?: number;
    staleTime?: number;
    backgroundRefresh?: boolean;
  } = {}
) => {
  const {
    cacheTime = 10 * 60 * 1000, // 10 minutes
    staleTime = 30 * 1000,      // 30 seconds
    backgroundRefresh = true
  } = options;
  
  const { data, loading, error, mutate } = useRequest(
    fetcher,
    {
      cacheKey: key,
      cacheTime,
      staleTime,
      refreshOnWindowFocus: backgroundRefresh,
      focusTimespan: staleTime
    }
  );
  
  // Prefetch related data in background
  const prefetch = useCallback((prefetchKey: string, prefetcher: () => Promise<any>) => {
    // Background prefetch logic
    requestIdleCallback(() => {
      useRequest(prefetcher, {
        cacheKey: prefetchKey,
        cacheTime,
        manual: false
      });
    });
  }, [cacheTime]);
  
  return { data, loading, error, mutate, prefetch };
};

// Usage
const ProductDetail = ({ productId }: { productId: string }) => {
  const { data: product, prefetch } = useSmartCache(
    `product-${productId}`,
    () => fetch(`/api/product/${productId}`).then(r => r.json()),
    { backgroundRefresh: true }
  );
  
  // Prefetch related products
  useEffect(() => {
    if (product?.categoryId) {
      prefetch(
        `category-${product.categoryId}`,
        () => fetch(`/api/category/${product.categoryId}`).then(r => r.json())
      );
    }
  }, [product?.categoryId, prefetch]);
  
  return <div>{/* Product UI */}</div>;
};

Performance Monitoring

// Request performance monitoring
const useMonitoredRequest = <T>(
  service: any,
  options?: BaseOptions<T, any[]>
) => {
  const startTime = useRef<number>();
  
  return useRequest<T>(service, {
    ...options,
    onSuccess: (data, params) => {
      const duration = Date.now() - (startTime.current || 0);
      
      // Log performance metrics
      analytics.track('request_success', {
        service: typeof service === 'string' ? service : 'function',
        duration,
        dataSize: JSON.stringify(data).length,
        params
      });
      
      if (duration > 3000) {
        console.warn(`Slow request detected: ${duration}ms`);
      }
      
      options?.onSuccess?.(data, params);
    },
    
    onError: (error, params) => {
      const duration = Date.now() - (startTime.current || 0);
      
      analytics.track('request_error', {
        service: typeof service === 'string' ? service : 'function',
        error: error.message,
        duration,
        params
      });
      
      options?.onError?.(error, params);
    }
  });
};

Configuration Reference

Complete Global Configuration

interface CompleteGlobalConfig {
  // Request method
  requestMethod?: (service: any) => Promise<any>;
  
  // Callbacks
  onSuccess?: (data: any, params: any[]) => void;
  onError?: (error: Error, params: any[]) => void;
  
  // Loading
  defaultLoading?: boolean;
  loadingDelay?: number;
  
  // Caching
  cacheTime?: number;
  staleTime?: number;
  
  // Polling
  pollingInterval?: number;
  pollingWhenHidden?: boolean;
  
  // Focus refresh
  refreshOnWindowFocus?: boolean;
  focusTimespan?: number;
  
  // Rate limiting
  debounceInterval?: number;
  throttleInterval?: number;
  
  // Error handling
  throwOnError?: boolean;
  
  // Conditional execution
  ready?: boolean;
  
  // Manual mode
  manual?: boolean;
  
  // Default parameters
  defaultParams?: any[];
  
  // Initial data
  initialData?: any;
}

Complete Configuration Example:

const comprehensiveConfig: CompleteGlobalConfig = {
  // Custom axios-based request method
  requestMethod: async (service) => {
    const axiosConfig = typeof service === 'string' 
      ? { url: service, method: 'GET' }
      : service;
      
    const response = await axios(axiosConfig);
    return response.data;
  },
  
  // Global success handling
  onSuccess: (data, params) => {
    console.log('✅ Request succeeded:', { 
      timestamp: new Date().toISOString(),
      dataKeys: Object.keys(data || {}),
      params 
    });
  },
  
  // Global error handling
  onError: (error, params) => {
    console.error('❌ Request failed:', {
      timestamp: new Date().toISOString(),
      error: error.message,
      params
    });
    
    // Send to error tracking
    errorTracker.captureException(error, { 
      extra: { params },
      tags: { component: 'useRequest' }
    });
  },
  
  // Loading configuration
  defaultLoading: false,
  loadingDelay: 200, // Show loading after 200ms
  
  // Caching strategy
  cacheTime: 5 * 60 * 1000,  // 5 minutes
  staleTime: 30 * 1000,      // 30 seconds
  
  // Polling disabled by default
  pollingInterval: 0,
  pollingWhenHidden: false,
  
  // Window focus refresh
  refreshOnWindowFocus: true,
  focusTimespan: 5000, // Don't refresh more than once per 5 seconds
  
  // No rate limiting by default
  debounceInterval: undefined,
  throttleInterval: undefined,
  
  // Error handling
  throwOnError: false, // Catch errors by default
  
  // Ready by default
  ready: true,
  
  // Auto-execute by default
  manual: false
};

<UseRequestProvider value={comprehensiveConfig}>
  <App />
</UseRequestProvider>