Skip to content

How to handle an async calculation

This example demonstrates how to handle an async calculation using the useAsync hook.

Watch the live preview.

tsx
import {useAsync} from './useAsync';

const calculateOperationStats = async () => {
    return new Promise<{ total: number; average: number; highest: number }>(
        (resolve) => {
            const operations = Array.from({length: 100000}, () =>
                Math.floor(Math.random() * 1000)
            );

            setTimeout(() => {
                const total = operations.reduce((sum, value) => sum + value, 0);
                const average = total / operations.length;
                const highest = Math.max(...operations);
                resolve({total, average, highest});
            }, 2000); // Simulate an expensive calculation
        }
    );
};

export default function App() {
    const {execute, data, isLoading, error, isSuccess} = useAsync<{
        total: number;
        average: number;
        highest: number;
    }>();

    return (
        <div>
            <button
                onClick={() => execute(calculateOperationStats)}
                disabled={isLoading}
            >
                {isLoading ? 'Calculating...' : 'Calculate Stats'}
            </button>
            {isLoading && <p>Calculating statistics for 100,000 operations...</p>}
            {error && <p>Error: {error.message}</p>}
            {isSuccess && data && (
                <div>
                    <p>Statistics:</p>
                    <ul>
                        <li>Total: {data.total}</li>
                        <li>Average: {data.average.toFixed(2)}</li>
                        <li>Highest Value: {data.highest}</li>
                    </ul>
                </div>
            )}
        </div>
    );
};
typescript
import {useState, useCallback} from 'react';

interface UseAsyncState<T> {
    data: T | null;
    isLoading: boolean;
    error: Error | null;
    isSuccess: boolean;
}

export const useAsync = <T>() => {
    const [state, setState] = useState<UseAsyncState<T>>({
        data: null,
        isLoading: false,
        error: null,
        isSuccess: false
    });

    const execute = useCallback(async (asyncFunction: () => Promise<T>) => {
        setState((prev) => ({...prev, isLoading: true, error: null}));

        try {
            const result = await asyncFunction();
            setState({
                data: result,
                isLoading: false,
                error: null,
                isSuccess: true
            });
            return result;
        } catch (error) {
            setState({
                data: null,
                isLoading: false,
                error: error as Error,
                isSuccess: false
            });
            throw error;
        }
    }, []);

    return {execute, ...state};
};