useAsync
A generic wrapper for any async function or Promise. Handle loading, error, and data states with ease.
Live Demo
Try different async operations: manual execution, immediate loading, error handling, and state reset.
🎯 Manual Execution
Execute async function on demand
Click "Fetch User" to load data
⚡ Immediate Execution
Auto-execute on mount with immediate: true
🐌 Slow Operation
Simulate long-running async operations
Click to start
❌ Error Handling
Gracefully handle async errors
Click to trigger an error
🔢 Calculation with Reset
Calculate factorial and reset state
Enter a number and click Calculate
💡 Features Demonstrated:
- Manual execution with execute() function
- Immediate execution on mount
- Loading, error, and data states
- Error handling and recovery
- Reset functionality to clear state
- Works with any async function or Promise
Code Examples
Basic Usage
import { useAsync } from "@uiblock/hooks";
const fetchUser = async (userId: number) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
};
export default function UserProfile() {
const { data, loading, error, execute } = useAsync(fetchUser);
return (
<div>
<button onClick={() => execute(1)} disabled={loading}>
Load User
</button>
{loading && <p>Loading...</p>}
{error && <p>Error: {error.message}</p>}
{data && <div>{data.name}</div>}
</div>
);
}Immediate Execution
import { useAsync } from "@uiblock/hooks";
const fetchInitialData = async () => {
const response = await fetch('/api/initial-data');
return response.json();
};
export default function Dashboard() {
const { data, loading, error } = useAsync(fetchInitialData, {
immediate: true // Execute on mount
});
if (loading) return <div>Loading dashboard...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{/* Render dashboard with data */}</div>;
}With Callbacks
import { useAsync } from "@uiblock/hooks";
const saveData = async (data: any) => {
const response = await fetch('/api/save', {
method: 'POST',
body: JSON.stringify(data)
});
return response.json();
};
export default function SaveForm() {
const { loading, execute } = useAsync(saveData, {
onSuccess: (data) => {
console.log('Saved successfully:', data);
alert('Data saved!');
},
onError: (error) => {
console.error('Save failed:', error);
alert('Failed to save');
}
});
const handleSubmit = (formData: any) => {
execute(formData);
};
return (
<form onSubmit={(e) => {
e.preventDefault();
handleSubmit({ /* form data */ });
}}>
<button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save'}
</button>
</form>
);
}Reset State
import { useAsync } from "@uiblock/hooks";
const searchUsers = async (query: string) => {
const response = await fetch(`/api/search?q=${query}`);
return response.json();
};
export default function SearchBox() {
const { data, loading, execute, reset } = useAsync(searchUsers);
const handleSearch = (query: string) => {
if (query) {
execute(query);
} else {
reset(); // Clear results when query is empty
}
};
return (
<div>
<input
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search users..."
/>
<button onClick={reset}>Clear</button>
{loading && <p>Searching...</p>}
{data && <div>{/* Render results */}</div>}
</div>
);
}Multiple Async Operations
import { useAsync } from "@uiblock/hooks";
const fetchUser = async (id: number) => { /* ... */ };
const fetchPosts = async (userId: number) => { /* ... */ };
export default function UserDashboard() {
const {
data: user,
loading: userLoading,
execute: loadUser
} = useAsync(fetchUser);
const {
data: posts,
loading: postsLoading,
execute: loadPosts
} = useAsync(fetchPosts);
const loadUserData = async (userId: number) => {
await loadUser(userId);
await loadPosts(userId);
};
return (
<div>
<button onClick={() => loadUserData(1)}>
Load User Dashboard
</button>
{(userLoading || postsLoading) && <p>Loading...</p>}
{user && <div>{user.name}</div>}
{posts && <div>{posts.length} posts</div>}
</div>
);
}How It Works
Here's the implementation:
import { useState, useEffect, useCallback, useRef } from 'react'
export interface UseAsyncOptions<T> {
immediate?: boolean
onSuccess?: (data: T) => void
onError?: (error: Error) => void
}
export interface UseAsyncResult<T> {
data: T | null
error: Error | null
loading: boolean
execute: (...args: any[]) => Promise<T | null>
reset: () => void
}
export function useAsync<T = any>(
asyncFunction: (...args: any[]) => Promise<T>,
options: UseAsyncOptions<T> = {}
): UseAsyncResult<T> {
const [data, setData] = useState<T | null>(null)
const [error, setError] = useState<Error | null>(null)
const [loading, setLoading] = useState<boolean>(false)
const { immediate = false, onSuccess, onError } = options
const mountedRef = useRef(true)
const asyncFunctionRef = useRef(asyncFunction)
useEffect(() => {
asyncFunctionRef.current = asyncFunction
}, [asyncFunction])
const execute = useCallback(
async (...args: any[]): Promise<T | null> => {
setLoading(true)
setError(null)
try {
const result = await asyncFunctionRef.current(...args)
if (mountedRef.current) {
setData(result)
setLoading(false)
onSuccess?.(result)
}
return result
} catch (err) {
const error = err instanceof Error ? err : new Error('An error occurred')
if (mountedRef.current) {
setError(error)
setLoading(false)
onError?.(error)
}
return null
}
},
[onSuccess, onError]
)
const reset = useCallback(() => {
setData(null)
setError(null)
setLoading(false)
}, [])
useEffect(() => {
if (immediate) {
execute()
}
}, [immediate, execute])
useEffect(() => {
return () => {
mountedRef.current = false
}
}, [])
return { data, error, loading, execute, reset }
}API Reference
Parameters
asyncFunction: (...args: any[]) => Promise<T>The async function to execute
options?: UseAsyncOptions<T>Optional configuration object
immediate: Execute function on mount (default: false)onSuccess: Callback when execution succeedsonError: Callback when execution fails
Return Value
data: T | nullThe result of the async function
error: Error | nullError object if execution failed
loading: booleanTrue while the async function is executing
execute: (...args: any[]) => Promise<T | null>Function to manually trigger execution
reset: () => voidFunction to reset all state (data, error, loading)
💡 Use Cases
- API calls and data fetching
- Form submissions
- File uploads
- Database operations
- Complex calculations
- Any async operation that needs loading/error states