request.ts 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import axios, { Canceler, AxiosInstance, AxiosResponse } from 'axios';
  2. import { RefObject, 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>): [
  11. (query: Query) => void,
  12. Response | null,
  13. boolean,
  14. RefObject<((unmount?: boolean) => void) | undefined>,
  15. ] {
  16. const [response, setResponse] = useState<Response | null>(null);
  17. const [loading, setLoading] = useState<boolean>(false);
  18. const cancel = useRef<(unmount?: boolean) => void>();
  19. useEffect(() => (): void => cancel.current?.(true), []);
  20. const onRequest = useCallback(
  21. (query: Query) => {
  22. let cancelled = false;
  23. let unmounted = false;
  24. let cancelRequest: Canceler | null = null;
  25. cancel.current?.();
  26. cancel.current = (unmount = false): void => {
  27. cancelled = true;
  28. unmounted = unmount;
  29. cancelRequest?.();
  30. };
  31. const axiosWithToken = axios.create({
  32. cancelToken: new axios.CancelToken((token): void => {
  33. cancelRequest = token;
  34. }),
  35. });
  36. const makeRequest = async (): Promise<void> => {
  37. try {
  38. setLoading(true);
  39. const res = await sendRequest(axiosWithToken, query);
  40. if (!cancelled) {
  41. setResponse(res.data);
  42. }
  43. } catch (err) {
  44. if (!axios.isCancel(err)) {
  45. onError?.(err);
  46. }
  47. } finally {
  48. if (!unmounted) {
  49. setLoading(false);
  50. }
  51. }
  52. };
  53. makeRequest();
  54. },
  55. [onError, sendRequest],
  56. );
  57. return [onRequest, response, loading, cancel];
  58. }