2022-03-06 13:42:27 +01:00
|
|
|
import React, { useMemo, useRef, useState } from "react";
|
2022-02-17 18:25:12 +01:00
|
|
|
|
|
|
|
export function useLoading<T extends (...args: any) => Promise<any>>(
|
|
|
|
action: T
|
|
|
|
) {
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const [success, setSuccess] = useState(false);
|
|
|
|
const [error, setError] = useState<any | undefined>(undefined);
|
2022-03-06 13:43:32 +01:00
|
|
|
const isMounted = useRef(true);
|
2022-03-06 13:42:27 +01:00
|
|
|
|
|
|
|
// we want action to be memoized forever
|
|
|
|
const actionMemo = useMemo(() => action, []); // eslint-disable-line react-hooks/exhaustive-deps
|
2022-02-17 18:25:12 +01:00
|
|
|
|
|
|
|
React.useEffect(() => {
|
2022-03-06 13:42:27 +01:00
|
|
|
isMounted.current = true;
|
2022-02-17 18:25:12 +01:00
|
|
|
return () => {
|
2022-03-06 13:42:27 +01:00
|
|
|
isMounted.current = false;
|
2022-02-17 18:25:12 +01:00
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
|
2022-03-06 13:42:27 +01:00
|
|
|
const doAction = useMemo(
|
|
|
|
() =>
|
|
|
|
async (...args: Parameters<T>) => {
|
|
|
|
setLoading(true);
|
|
|
|
setSuccess(false);
|
|
|
|
setError(undefined);
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
actionMemo(...args)
|
|
|
|
.then((v) => {
|
|
|
|
if (!isMounted.current) return resolve(undefined);
|
|
|
|
setSuccess(true);
|
|
|
|
resolve(v);
|
2022-03-06 14:41:51 +01:00
|
|
|
return null;
|
2022-03-06 13:42:27 +01:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
if (isMounted) {
|
|
|
|
setError(err);
|
|
|
|
setSuccess(false);
|
|
|
|
}
|
|
|
|
resolve(undefined);
|
|
|
|
});
|
|
|
|
}).finally(() => isMounted.current && setLoading(false));
|
|
|
|
},
|
|
|
|
[actionMemo]
|
|
|
|
);
|
2022-02-17 18:25:12 +01:00
|
|
|
return [doAction, loading, error, success];
|
|
|
|
}
|