JAVASCRIPT
Handle Asynchronous Operations with a Custom useAsync Hook
Create a versatile custom React hook to manage the state of any asynchronous operation, centralizing loading, error, and data states for cleaner and more robust component logic.
import React, { useState, useEffect } from 'react';
// Custom Hook: useAsync
const useAsync = (asyncFunction, immediate = true) => {
const [status, setStatus] = useState('idle'); // 'idle', 'pending', 'success', 'error'
const [value, setValue] = useState(null);
const [error, setError] = useState(null);
// The execute function calls the async function and sets the state
const execute = React.useCallback(() => {
setStatus('pending');
setValue(null);
setError(null);
return asyncFunction()
.then((response) => {
setValue(response);
setStatus('success');
})
.catch((error) => {
setError(error);
setStatus('error');
});
}, [asyncFunction]); // Recreate if asyncFunction changes
// Call execute if we want to fire it right away
useEffect(() => {
if (immediate) {
execute();
}
}, [execute, immediate]);
return { execute, status, value, error };
};
// Example Usage:
const fetchUserData = () =>
new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve({ name: 'John Doe', email: '[email protected]' });
} else {
reject(new Error('Failed to fetch user data!'));
}
}, 1500);
});
const UserProfile = () => {
const { execute, status, value, error } = useAsync(fetchUserData, false); // Not immediate
return (
<div>
<h1>User Profile</h1>
{status === 'idle' && <button onClick={execute}>Load User Data</button>}
{status === 'pending' && <p>Loading user data...</p>}
{status === 'error' && <p style={{ color: 'red' }}>Error: {error?.message}</p>}
{status === 'success' && value && (
<div>
<p>Name: {value.name}</p>
<p>Email: {value.email}</p>
</div>
)}
{status !== 'pending' && status !== 'idle' && (
<button onClick={execute}>Reload User Data</button>
)}
</div>
);
};
const App = () => <UserProfile />;
How it works: The `useAsync` hook provides a structured way to handle asynchronous operations (like API calls) within React components. It manages `status` (`idle`, `pending`, `success`, `error`), `value` (the data returned), and `error` states. The `execute` function runs the provided `asyncFunction`. This centralizes common async logic, making components cleaner by abstracting away the boilerplate for loading, error, and data states. The `immediate` flag allows control over whether the function runs on mount or needs to be triggered manually.