request.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import axios, { Canceler, AxiosInstance, AxiosResponse } from 'axios';
  2. import { useCallback, useEffect, useRef, useState } from 'react';
  3. type Options<Query, Response> = {
  4. sendRequest: (axiosInstance: AxiosInstance, query: Query) => Promise<AxiosResponse<Response>>;
  5. onError?: (err: Error) => void;
  6. };
  7. export function useRequestCallback<Query, Response = void>({
  8. onError,
  9. sendRequest,
  10. }: Options<Query, Response>): [(query: Query) => void, Response | null, boolean] {
  11. const [response, setResponse] = useState<Response | null>(null);
  12. const [loading, setLoading] = useState<boolean>(false);
  13. const cancel = useRef<() => void>();
  14. useEffect(() => (): void => cancel.current?.(), []);
  15. const onRequest = useCallback(
  16. (query: Query) => {
  17. let cancelled = false;
  18. let cancelRequest: Canceler | null = null;
  19. cancel.current?.();
  20. cancel.current = (): void => {
  21. cancelled = true;
  22. cancelRequest?.();
  23. };
  24. const axiosWithToken = axios.create({
  25. cancelToken: new axios.CancelToken((token): void => {
  26. cancelRequest = token;
  27. }),
  28. });
  29. const makeRequest = async (): Promise<void> => {
  30. try {
  31. setLoading(true);
  32. const res = await sendRequest(axiosWithToken, query);
  33. if (!cancelled) {
  34. setResponse(res.data);
  35. }
  36. } catch (err) {
  37. if (!axios.isCancel(err)) {
  38. onError?.(err);
  39. }
  40. } finally {
  41. if (!cancelled) {
  42. setLoading(false);
  43. }
  44. }
  45. };
  46. makeRequest();
  47. },
  48. [onError, sendRequest],
  49. );
  50. return [onRequest, response, loading];
  51. }