Global configuration, context providers, advanced options, and specialized hooks for complex application requirements and enterprise-level data management patterns.
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>;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 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
}
);// 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>// 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>
);
};// 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>;
};// 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
})
}
);
};// 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
};
};// 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>;
};// 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);
}
});
};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>