import axios, { Canceler, AxiosInstance, AxiosResponse } from 'axios'; import { useEffect, useRef, useState } from 'react'; type Options = { query: Query; pause?: boolean; sendRequest: (axiosInstance: AxiosInstance, query: Query) => Promise>; handleResponse: (res: Response, query: Query) => void; onError?: (err: Error) => void; onClear?: () => void; debounceDelay?: number; }; export function useCancellableRequest({ query, pause, sendRequest, handleResponse, onError, onClear, }: Options): boolean { const [loading, setLoading] = useState(false); const cancelRequest = useRef(); useEffect(() => { setLoading(!!query); if (!query) { onClear?.(); } }, [query, onClear]); useEffect(() => { let cancelled = false; const request = async (): Promise => { try { const axiosWithToken = axios.create({ cancelToken: new axios.CancelToken((token): void => { cancelRequest.current = token; }), }); const res = await sendRequest(axiosWithToken, query); if (cancelled) { return; } handleResponse(res.data, query); } catch (err) { if (!axios.isCancel(err)) { onError?.(err); } } finally { if (!cancelled) { setLoading(false); } } }; if (!pause) { request(); } return (): void => { cancelled = true; if (cancelRequest.current) { cancelRequest.current(); } }; }, [sendRequest, handleResponse, onError, query, pause]); return loading; }