Browse Source

[ui-core]: add onSuccess and onError callback for useLoadData hook (#3849)

* [ui-core]: add onSuccess and onError callback for useLoadData hook

* fix the interfacing naming convention
Ram Prasad Agarwal 1 year ago
parent
commit
9f0280ea9d

+ 58 - 7
desktop/core/src/desktop/js/utils/hooks/useLoadData.test.tsx

@@ -66,7 +66,7 @@ describe('useLoadData', () => {
     expect(result.current.error).toBeUndefined();
     expect(result.current.loading).toBe(true);
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledWith(mockUrl, mockOptions.params, expect.any(Object));
       expect(result.current.data).toEqual(mockData);
       expect(result.current.error).toBeUndefined();
@@ -84,7 +84,7 @@ describe('useLoadData', () => {
     expect(result.current.error).toBeUndefined();
     expect(result.current.loading).toBe(true);
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledWith(mockUrl, undefined, expect.any(Object));
       expect(result.current.data).toBeUndefined();
       expect(result.current.error).toEqual(mockError);
@@ -108,7 +108,7 @@ describe('useLoadData', () => {
     expect(result.current.error).toBeUndefined();
     expect(result.current.loading).toBe(true);
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledWith(mockUrl, undefined, expect.any(Object));
       expect(result.current.data).toEqual(mockData);
       expect(result.current.error).toBeUndefined();
@@ -121,7 +121,7 @@ describe('useLoadData', () => {
       result.current.reloadData();
     });
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledTimes(2);
       expect(mockGet).toHaveBeenCalledWith(mockUrl, undefined, expect.any(Object));
       expect(result.current.data).toEqual({ ...mockData, product: 'Hue 2' });
@@ -137,7 +137,7 @@ describe('useLoadData', () => {
     expect(result.current.error).toBeUndefined();
     expect(result.current.loading).toBe(true);
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledWith(mockUrl, undefined, expect.any(Object));
       expect(result.current.data).toEqual(mockData);
       expect(result.current.error).toBeUndefined();
@@ -157,7 +157,7 @@ describe('useLoadData', () => {
     expect(result.current.error).toBeUndefined();
     expect(result.current.loading).toBe(true);
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledWith(mockUrl, mockOptions.params, expect.any(Object));
       expect(result.current.data).toEqual(mockData);
       expect(result.current.error).toBeUndefined();
@@ -172,11 +172,62 @@ describe('useLoadData', () => {
 
     rerender({ url: mockUrl, options: newOptions });
 
-    waitFor(() => {
+    await waitFor(() => {
       expect(mockGet).toHaveBeenCalledWith(mockUrl, newOptions.params, expect.any(Object));
       expect(result.current.data).toEqual(newMockData);
       expect(result.current.error).toBeUndefined();
       expect(result.current.loading).toBe(false);
     });
   });
+
+  it('should call onSuccess callback', async () => {
+    const mockOnSuccess = jest.fn();
+    const mockOnError = jest.fn();
+    const { result } = renderHook(() =>
+      useLoadData(mockUrl, {
+        onSuccess: mockOnSuccess,
+        onError: mockOnError
+      })
+    );
+
+    expect(result.current.data).toBeUndefined();
+    expect(result.current.error).toBeUndefined();
+    expect(result.current.loading).toBe(true);
+
+    await waitFor(() => {
+      expect(mockGet).toHaveBeenCalledWith(mockUrl, undefined, expect.any(Object));
+      expect(result.current.data).toEqual(mockData);
+      expect(result.current.error).toBeUndefined();
+      expect(result.current.loading).toBe(false);
+      expect(mockOnSuccess).toHaveBeenCalledWith(mockData);
+      expect(mockOnError).not.toHaveBeenCalled();
+    });
+  });
+
+  it('should call onError callback', async () => {
+    const mockError = new Error('Fetch error');
+    mockGet.mockRejectedValue(mockError);
+
+    const mockOnSuccess = jest.fn();
+    const mockOnError = jest.fn();
+    const { result } = renderHook(() =>
+      useLoadData(mockUrl, {
+        onSuccess: mockOnSuccess,
+        onError: mockOnError
+      })
+    );
+
+    expect(result.current.data).toBeUndefined();
+    expect(result.current.error).toBeUndefined();
+    expect(result.current.loading).toBe(true);
+
+    await waitFor(() => {
+      expect(mockGet).toHaveBeenCalledWith(mockUrl, undefined, expect.any(Object));
+      expect(result.current.data).toBeUndefined();
+      expect(result.current.error).toEqual(mockError);
+      expect(result.current.loading).toBe(false);
+      expect(mockOnSuccess).not.toHaveBeenCalled();
+      expect(mockOnError).toHaveBeenCalledWith(mockError);
+    });
+  });
 });

+ 17 - 6
desktop/core/src/desktop/js/utils/hooks/useLoadData.ts

@@ -17,22 +17,27 @@
 import { useCallback, useEffect, useMemo, useState } from 'react';
 import { ApiFetchOptions, get } from '../../api/utils';
 
-export type IOptions<T, U> = {
+export interface Options<T, U> {
   urlPrefix?: string;
   params?: U;
   fetchOptions?: ApiFetchOptions<T>;
   skip?: boolean;
-};
+  onSuccess?: (data: T) => void;
+  onError?: (error: Error) => void;
+}
 
-type IUseLoadData<T> = {
+interface UseLoadDataProps<T> {
   data?: T;
   loading: boolean;
   error?: Error;
   reloadData: () => void;
-};
+}
 
-const useLoadData = <T, U = unknown>(url?: string, options?: IOptions<T, U>): IUseLoadData<T> => {
-  const [localOptions, setLocalOptions] = useState<IOptions<T, U> | undefined>(options);
+const useLoadData = <T, U = unknown>(
+  url?: string,
+  options?: Options<T, U>
+): UseLoadDataProps<T> => {
+  const [localOptions, setLocalOptions] = useState<Options<T, U> | undefined>(options);
   const [data, setData] = useState<T | undefined>();
   const [loading, setLoading] = useState<boolean>(false);
   const [error, setError] = useState<Error | undefined>();
@@ -61,8 +66,14 @@ const useLoadData = <T, U = unknown>(url?: string, options?: IOptions<T, U>): IU
         const fetchUrl = localOptions?.urlPrefix ? `${localOptions.urlPrefix}${url}` : url;
         const response = await get<T, U>(fetchUrl, localOptions?.params, fetchOptions);
         setData(response);
+        if (localOptions?.onSuccess) {
+          localOptions.onSuccess(response);
+        }
       } catch (error) {
         setError(error instanceof Error ? error : new Error(error));
+        if (localOptions?.onError) {
+          localOptions.onError(error);
+        }
       } finally {
         setLoading(false);
       }