From 3add2b7a9476fa1990109a824245ad2eee9703e5 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Sat, 3 Aug 2024 23:09:08 -0700 Subject: [PATCH] useAsyncCall: simplifies some state tracking --- src/common/util/hooks/useAsyncCall.ts | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/common/util/hooks/useAsyncCall.ts diff --git a/src/common/util/hooks/useAsyncCall.ts b/src/common/util/hooks/useAsyncCall.ts new file mode 100644 index 000000000..c666dac4d --- /dev/null +++ b/src/common/util/hooks/useAsyncCall.ts @@ -0,0 +1,49 @@ +import * as React from 'react'; + +interface AsyncState { + isLoading: boolean; + error: Error | null; + data: T | null; +} + +export function useAsyncCall Promise>(asyncFunction: T) { + const [state, setState] = React.useState>>>({ + isLoading: false, + error: null, + data: null, + }); + + const isMounted = React.useRef(true); + + React.useEffect(() => { + return () => { + isMounted.current = false; + }; + }, []); + + const execute = React.useCallback( + async (...args: Parameters) => { + setState(prev => ({ ...prev, isLoading: true, error: null })); + + try { + const result = await asyncFunction(...args); + if (isMounted.current) { + setState({ isLoading: false, error: null, data: result }); + } + return result; + } catch (err) { + if (isMounted.current) { + setState(prev => ({ + ...prev, + isLoading: false, + error: err instanceof Error ? err : new Error('An error occurred'), + })); + } + throw err; + } + }, + [asyncFunction], + ); + + return [state.isLoading, execute, state.error, state.data] as const; +} \ No newline at end of file