2020-11-07 19:14:41 +01:00
|
|
|
import { useCallback, useEffect, useState } from "react";
|
2020-06-05 01:22:04 +02:00
|
|
|
|
|
|
|
|
type UseAsync = <T>(
|
|
|
|
|
asyncFunction: () => Promise<T>,
|
|
|
|
|
initialValue: T
|
|
|
|
|
) => { pending: boolean; value: T; error: Error | null };
|
|
|
|
|
|
|
|
|
|
export const useAsync: UseAsync = <T extends any>(
|
|
|
|
|
asyncFunction: () => Promise<T>,
|
|
|
|
|
initialValue: T
|
|
|
|
|
) => {
|
|
|
|
|
const [pending, setPending] = useState(false);
|
|
|
|
|
const [value, setValue] = useState<T>(initialValue);
|
|
|
|
|
const [error, setError] = useState(null);
|
|
|
|
|
|
|
|
|
|
// The execute function wraps asyncFunction and
|
|
|
|
|
// handles setting state for pending, value, and error.
|
|
|
|
|
// useCallback ensures the below useEffect is not called
|
|
|
|
|
// on every render, but only if asyncFunction changes.
|
|
|
|
|
const execute = useCallback(() => {
|
|
|
|
|
setPending(true);
|
|
|
|
|
setValue(initialValue);
|
|
|
|
|
setError(null);
|
|
|
|
|
return asyncFunction()
|
|
|
|
|
.then((response) => setValue(response))
|
|
|
|
|
.catch((err) => setError(err))
|
|
|
|
|
.finally(() => setPending(false));
|
2020-11-07 19:14:41 +01:00
|
|
|
}, [asyncFunction, initialValue]);
|
2020-06-05 01:22:04 +02:00
|
|
|
|
|
|
|
|
// Call execute if we want to fire it right away.
|
|
|
|
|
// Otherwise execute can be called later, such as
|
|
|
|
|
// in an onClick handler.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
execute();
|
|
|
|
|
}, [execute]);
|
|
|
|
|
|
|
|
|
|
return { execute, pending, value, error };
|
|
|
|
|
};
|