React Hooks for fetching, caching and updating asynchronous data
—
Core async data fetching functionality with comprehensive state management, caching, request lifecycle control, and advanced features like polling, debouncing, and concurrent request handling.
Main hook for data fetching with comprehensive configuration options. Automatically handles request state, caching, polling, and lifecycle management.
/**
* Main useRequest hook for async data fetching
* @param service - URL string, request object, or async function
* @param options - Configuration options for request behavior
* @returns Request state and control functions
*/
function useRequest<R = any, P extends any[] = any>(
service: CombineService<R, P>,
options?: BaseOptions<R, P>
): BaseResult<R, P>;
/**
* useRequest with response formatting
* @param service - URL string, request object, or async function
* @param options - Configuration options with formatResult function
* @returns Request state with formatted data
*/
function useRequest<R = any, P extends any[] = any, U = any, UU extends U = any>(
service: CombineService<R, P>,
options: OptionsWithFormat<R, P, U, UU>
): BaseResult<U, P>;Usage Examples:
import useRequest from "@ahooksjs/use-request";
// Simple URL string
const { data, loading, error } = useRequest('/api/users');
// Function service with parameters
const { data, loading, run } = useRequest(
(id: string) => `/api/user/${id}`,
{ manual: true }
);
run('123');
// Request object
const { data, loading } = useRequest({
url: '/api/users',
method: 'POST',
body: JSON.stringify({ name: 'John' }),
headers: { 'Content-Type': 'application/json' }
});
// Async function service
const { data, loading } = useRequest(async (query: string) => {
const response = await fetch(`/api/search?q=${query}`);
return response.json();
});
// With response formatting
const { data, loading } = useRequest('/api/user', {
formatResult: (response) => ({
id: response.data.id,
name: response.data.full_name,
email: response.data.email_address
})
});Core async hook handling basic data fetching, caching, and state management. This is the underlying implementation used by useRequest.
/**
* Core async data fetching hook
* @param service - Async function to execute
* @param options - Configuration options
* @returns Request state and control functions
*/
function useAsync<R, P extends any[]>(
service: Service<R, P>,
options?: BaseOptions<R, P>
): BaseResult<R, P>;
/**
* useAsync with response formatting
* @param service - Async function to execute
* @param options - Configuration options with formatResult
* @returns Request state with formatted data
*/
function useAsync<R, P extends any[], U, UU extends U = any>(
service: Service<R, P>,
options: OptionsWithFormat<R, P, U, UU>
): BaseResult<U, P>;Usage Examples:
import { useAsync } from "@ahooksjs/use-request";
// Basic usage
const { data, loading, error, run } = useAsync(
async (userId: string) => {
const response = await fetch(`/api/user/${userId}`);
return response.json();
},
{ manual: true }
);
// Execute the async function
run('123');interface BaseOptions<R, P extends any[]> {
/** Dependencies that trigger refresh */
refreshDeps?: DependencyList;
/** Manual trigger mode - prevents automatic execution */
manual?: boolean;
/** Success callback */
onSuccess?: (data: R, params: P) => void;
/** Error callback */
onError?: (e: Error, params: P) => void;
/** Initial loading state */
defaultLoading?: boolean;
/** Delay before showing loading state (ms) */
loadingDelay?: number;
/** Default parameters for automatic execution */
defaultParams?: P;
/** Initial data value */
initialData?: R;
/** Ready state for conditional requests */
ready?: boolean;
/** Throw errors instead of catching them */
throwOnError?: boolean;
}interface PollingOptions {
/** Polling interval in milliseconds */
pollingInterval?: number;
/** Continue polling when window/tab is hidden */
pollingWhenHidden?: boolean;
}Usage Examples:
// Polling every 5 seconds
const { data } = useRequest('/api/status', {
pollingInterval: 5000
});
// Stop polling when window is hidden
const { data } = useRequest('/api/live-data', {
pollingInterval: 1000,
pollingWhenHidden: false
});interface FocusOptions {
/** Refresh when window gains focus */
refreshOnWindowFocus?: boolean;
/** Debounce time for focus refresh (ms) */
focusTimespan?: number;
}Usage Examples:
// Refresh data when user returns to tab
const { data } = useRequest('/api/notifications', {
refreshOnWindowFocus: true,
focusTimespan: 5000 // Don't refresh more than once per 5 seconds
});interface CachingOptions {
/** Cache key for request caching */
cacheKey?: CachedKeyType;
/** Cache expiration time (ms) */
cacheTime?: number;
/** Stale data time threshold (ms) */
staleTime?: number;
}
type CachedKeyType = string | number;Usage Examples:
// Cache user data for 5 minutes
const { data } = useRequest('/api/user', {
cacheKey: 'user-profile',
cacheTime: 5 * 60 * 1000,
staleTime: 30 * 1000 // Consider stale after 30 seconds
});
// Cache with dynamic key
const { data } = useRequest(
(userId: string) => `/api/user/${userId}`,
{
cacheKey: (userId) => `user-${userId}`,
cacheTime: 5 * 60 * 1000
}
);interface RateLimitingOptions {
/** Debounce request execution (ms) */
debounceInterval?: number;
/** Throttle request execution (ms) */
throttleInterval?: number;
}Usage Examples:
// Debounce API calls for search
const { data, run } = useRequest(
(query: string) => `/api/search?q=${query}`,
{
manual: true,
debounceInterval: 300 // Wait 300ms after last call
}
);
// Throttle expensive operations
const { data, run } = useRequest(
(data: any) => '/api/process',
{
manual: true,
throttleInterval: 1000 // Maximum once per second
}
);interface ConcurrentOptions<P extends any[]> {
/** Generate key for multiple concurrent requests */
fetchKey?: (...args: P) => string;
}Usage Examples:
// Handle multiple user requests simultaneously
const { data, run, fetches } = useRequest(
(userId: string) => `/api/user/${userId}`,
{
manual: true,
fetchKey: (userId) => `user-${userId}`
}
);
// Execute multiple requests
run('user1');
run('user2');
run('user3');
// Access individual request states
console.log(fetches['user-user1']); // State for user1 request
console.log(fetches['user-user2']); // State for user2 requestinterface CustomRequestOptions {
/** Custom request implementation */
requestMethod?: (service: any) => Promise<any>;
}Usage Examples:
import axios from 'axios';
// Use axios instead of fetch
const { data } = useRequest('/api/data', {
requestMethod: (url) => axios.get(url).then(res => res.data)
});
// Custom request with authentication
const { data } = useRequest('/api/secure-data', {
requestMethod: async (service) => {
const token = localStorage.getItem('auth-token');
if (typeof service === 'string') {
const response = await fetch(service, {
headers: { Authorization: `Bearer ${token}` }
});
return response.json();
}
return fetch(service.url, {
...service,
headers: {
...service.headers,
Authorization: `Bearer ${token}`
}
}).then(res => res.json());
}
});The complete result object returned by useRequest and useAsync.
interface BaseResult<R, P extends any[]> extends FetchResult<R, P> {
/** Reset all request state */
reset: () => void;
/** Multiple request instances (when using fetchKey) */
fetches: {
[key in string]: FetchResult<R, P>;
};
}
interface FetchResult<R, P extends any[]> {
/** Current loading state */
loading: boolean;
/** Response data */
data: R | undefined;
/** Request error */
error: Error | undefined;
/** Request parameters */
params: P;
/** Cancel current request */
cancel: () => void;
/** Refresh with same parameters */
refresh: () => Promise<R>;
/** Mutate data without request */
mutate: Mutate<R>;
/** Execute request with parameters */
run: (...args: P) => Promise<R>;
/** Cleanup function */
unmount: () => void;
}
type Mutate<R> = (x: R | undefined | ((data: R) => R)) => void;Usage Examples:
const {
data, // Current response data
loading, // Loading state
error, // Error object if request failed
params, // Parameters from last request
run, // Execute request manually
refresh, // Re-execute with same params
mutate, // Update data without request
cancel, // Cancel ongoing request
reset, // Reset all state
fetches, // Multiple request states
unmount // Cleanup
} = useRequest('/api/data');
// Mutate data optimistically
mutate(currentData => ({
...currentData,
updated: true
}));
// Reset to initial state
reset();
// Refresh current request
refresh();interface ErrorHandlingOptions<R, P extends any[]> {
/** Error callback */
onError?: (e: Error, params: P) => void;
/** Throw errors instead of catching */
throwOnError?: boolean;
}Usage Examples:
// Handle errors with callback
const { data, error } = useRequest('/api/data', {
onError: (error, params) => {
console.error('Request failed:', error.message);
// Log to error tracking service
errorTracker.log(error, { params });
}
});
// Throw errors for custom handling
const { data } = useRequest('/api/data', {
throwOnError: true
});
// Manual error handling with try/catch
const { run } = useRequest(
(data) => '/api/save',
{
manual: true,
throwOnError: true
}
);
try {
await run(formData);
showSuccessMessage();
} catch (error) {
showErrorMessage(error.message);
}// Automatic cleanup on unmount
const { data } = useRequest('/api/data', {
pollingInterval: 1000
}); // Polling stops when component unmounts
// Manual cleanup
const { unmount } = useRequest('/api/data');
useEffect(() => {
return () => {
unmount(); // Clean up manually if needed
};
}, []);// Conditional requests
const [userId, setUserId] = useState<string>();
const { data } = useRequest(
() => `/api/user/${userId}`,
{
ready: !!userId, // Only execute when userId exists
refreshDeps: [userId] // Re-execute when userId changes
}
);
// Dependent requests
const { data: user } = useRequest('/api/user');
const { data: posts } = useRequest(
() => `/api/posts?userId=${user?.id}`,
{
ready: !!user?.id, // Wait for user data
refreshDeps: [user?.id]
}
);Install with Tessl CLI
npx tessl i tessl/npm-ahooksjs--use-request