Pārlūkot izejas kodu

feat: return cancel callback from request hook

Fela Maslen 5 gadi atpakaļ
vecāks
revīzija
2a3b80b8c8
2 mainītis faili ar 37 papildinājumiem un 8 dzēšanām
  1. 23 1
      gmus-web/src/hooks/request.spec.tsx
  2. 14 7
      gmus-web/src/hooks/request.ts

+ 23 - 1
gmus-web/src/hooks/request.spec.tsx

@@ -14,7 +14,7 @@ describe(useRequestCallback.name, () => {
     axios.get(`http://my-api.url:1234/my/request?something=${query.something}`);
 
   const TestComponent: React.FC = () => {
-    const [onRequest, response, loading] = useRequestCallback<MyQuery, MyResponse>({
+    const [onRequest, response, loading, cancel] = useRequestCallback<MyQuery, MyResponse>({
       onError,
       sendRequest,
     });
@@ -22,6 +22,7 @@ describe(useRequestCallback.name, () => {
     return (
       <>
         <button onClick={(): void => onRequest({ something: 'yes' })}>Send request!</button>
+        <button onClick={(): void => cancel.current?.()}>Cancel!</button>
         <div data-testid="response">{JSON.stringify(response)}</div>
         <div data-testid="loading">{loading ? 'Loading' : 'Not loading'}</div>
       </>
@@ -78,6 +79,27 @@ describe(useRequestCallback.name, () => {
       expect(JSON.parse(getByTestId('response').innerHTML)).toStrictEqual({ result: 125 });
       unmount();
     });
+
+    describe('when the request is cancelled', () => {
+      it('should set the loading state back to false and not set the response', async () => {
+        expect.assertions(4);
+
+        const { getByText, getByTestId, unmount } = setupRequest();
+        act(() => {
+          fireEvent.click(getByText('Cancel!'));
+        });
+
+        expect(getByTestId('loading')).toHaveTextContent('Loading');
+
+        await waitFor(() => {
+          expect(getByTestId('loading')).toHaveTextContent('Not loading');
+        });
+
+        expect(JSON.parse(getByTestId('response').innerHTML)).toBeNull();
+
+        unmount();
+      });
+    });
   });
 
   describe('when an error occurs', () => {

+ 14 - 7
gmus-web/src/hooks/request.ts

@@ -1,5 +1,5 @@
 import axios, { Canceler, AxiosInstance, AxiosResponse } from 'axios';
-import { useCallback, useEffect, useRef, useState } from 'react';
+import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
 
 type Options<Query, Response> = {
   sendRequest: (axiosInstance: AxiosInstance, query: Query) => Promise<AxiosResponse<Response>>;
@@ -9,23 +9,30 @@ type Options<Query, Response> = {
 export function useRequestCallback<Query, Response = void>({
   onError,
   sendRequest,
-}: Options<Query, Response>): [(query: Query) => void, Response | null, boolean] {
+}: Options<Query, Response>): [
+  (query: Query) => void,
+  Response | null,
+  boolean,
+  RefObject<((unmount?: boolean) => void) | undefined>,
+] {
   const [response, setResponse] = useState<Response | null>(null);
   const [loading, setLoading] = useState<boolean>(false);
 
-  const cancel = useRef<() => void>();
+  const cancel = useRef<(unmount?: boolean) => void>();
 
-  useEffect(() => (): void => cancel.current?.(), []);
+  useEffect(() => (): void => cancel.current?.(true), []);
 
   const onRequest = useCallback(
     (query: Query) => {
       let cancelled = false;
+      let unmounted = false;
 
       let cancelRequest: Canceler | null = null;
 
       cancel.current?.();
-      cancel.current = (): void => {
+      cancel.current = (unmount = false): void => {
         cancelled = true;
+        unmounted = unmount;
         cancelRequest?.();
       };
 
@@ -48,7 +55,7 @@ export function useRequestCallback<Query, Response = void>({
             onError?.(err);
           }
         } finally {
-          if (!cancelled) {
+          if (!unmounted) {
             setLoading(false);
           }
         }
@@ -59,5 +66,5 @@ export function useRequestCallback<Query, Response = void>({
     [onError, sendRequest],
   );
 
-  return [onRequest, response, loading];
+  return [onRequest, response, loading, cancel];
 }