Skip to content

Instantly share code, notes, and snippets.

@ecesar88
Last active April 26, 2024 16:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ecesar88/1fa0abc99ff007ee2196271962c873ef to your computer and use it in GitHub Desktop.
Save ecesar88/1fa0abc99ff007ee2196271962c873ef to your computer and use it in GitHub Desktop.
import { AxiosError } from 'axios';
import { useCallback, useEffect, useRef, useState } from 'react';
interface useApiOptions<T = object, Args extends any[] = any[]> {
queryFn: (...args: Args) => Promise<T>;
onComplete?: (data: NonNullable<T>) => void;
immediate?: boolean;
}
type ErrorType = {
name: string;
reason?: string;
} | null;
const useQuery = <
T = Awaited<ReturnType<useApiOptions['queryFn']>>,
Args extends any[] = [],
>(
options: useApiOptions<T, Args>,
watch?: any[]
) => {
const [response, setResponse] = useState<T>();
const [error, setError] = useState<ErrorType>(null);
const [isLoading, setIsLoading] = useState(false);
const data = useRef<T>();
const refetch = useCallback(
async (...args: Args) => {
setIsLoading(true);
try {
data.current = await options.queryFn(...args);
setResponse(() => {
setError(null);
return data.current as T;
});
options?.onComplete?.(data.current as NonNullable<T>);
} catch (error) {
setError(() => {
if (error instanceof AxiosError) {
return {
name: error.name,
reason: error.message,
};
}
return {
name: 'Network Error',
};
});
} finally {
setIsLoading(false);
return data.current as T;
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[options.queryFn, options.onComplete]
);
useEffect(() => {
if (options.immediate === false) return;
refetch(...([] as any));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [options.immediate, JSON.stringify(watch)]);
return {
refetch,
isLoading,
error,
data: response as NonNullable<T>,
};
};
export default useQuery;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment