JAVASCRIPT
Simplify Async Operations with useAsync React Hook
Build a versatile useAsync React hook to manage asynchronous data fetching or any promise-based operation, gracefully handling loading states, successful data, and errors.
import { useState, useEffect, useCallback } from 'react';
function useAsync(asyncFunction, immediate = true) {
const [status, setStatus] = useState('idle');
const [value, setValue] = useState(null);
const [error, setError] = useState(null);
// The execute function calls the async function and sets the state
// We use useCallback to memoize the function, preventing unnecessary re-renders
// if it's passed down to child components.
const execute = useCallback(() => {
setStatus('pending');
setValue(null);
setError(null);
return asyncFunction()
.then((response) => {
setValue(response);
setStatus('success');
})
.catch((err) => {
setError(err);
setStatus('error');
});
}, [asyncFunction]);
// Call execute if we want to fire it right away.
// Otherwise, the caller can execute it themselves.
useEffect(() => {
if (immediate) {
execute();
}
}, [execute, immediate]);
return { execute, status, value, error };
}
// Example Usage:
// function DataFetcher() {
// const fetchData = useCallback(() => {
// return new Promise((resolve) => {
// setTimeout(() => resolve('Data fetched!'), 2000);
// });
// }, []);
//
// const { execute, status, value, error } = useAsync(fetchData, false);
//
// if (status === 'idle') {
// return <button onClick={execute}>Start Fetching</button>;
// }
//
// if (status === 'pending') {
// return <p>Loading...</p>;
// }
//
// if (status === 'error') {
// return <p>Error: {error.message}</p>;
// }
//
// if (status === 'success') {
// return <p>Success: {value}</p>;
// }
// }
How it works: The `useAsync` hook streamlines the process of managing asynchronous operations (like API calls) within React components. It takes an `asyncFunction` (which should return a Promise) and an optional `immediate` flag. The hook provides `status` (`idle`, `pending`, `success`, `error`), `value` (resolved data), `error` (rejection reason), and an `execute` function to trigger the async operation. This centralizes state management for asynchronous flows, making components cleaner and more robust.