@squiz/generic-browser-lib 1.66.0 → 1.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @squiz/generic-browser-lib
2
2
 
3
+ ## 1.67.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7c324d7: Added ignorePrevious option to useAsync hook (to ignore previous data)
8
+
3
9
  ## 1.66.0
4
10
 
5
11
  ### Minor Changes
@@ -4,6 +4,8 @@ export type UseAsyncProps<TReturnType, TDefaultValueType> = {
4
4
  callback: (() => TReturnType | Promise<TReturnType>) | Array<() => TReturnType | Promise<TReturnType>>;
5
5
  /** The default value to populate the data as when initially mounted or reloading data. */
6
6
  defaultValue: TReturnType | TDefaultValueType;
7
+ /** Optional toggle to ignore previous async callback data */
8
+ ignorePrevious?: boolean;
7
9
  };
8
10
  /**
9
11
  * Hook for invoking async code and keeping track of its state.
@@ -13,7 +15,7 @@ export type UseAsyncProps<TReturnType, TDefaultValueType> = {
13
15
  * 2. When any of the `deps` change.
14
16
  * 3. When the `reload` function is called.
15
17
  */
16
- export declare const useAsync: <TReturnType, TDefaultValueType>({ callback, defaultValue }: UseAsyncProps<TReturnType, TDefaultValueType>, deps: DependencyList) => {
18
+ export declare const useAsync: <TReturnType, TDefaultValueType>({ callback, defaultValue, ignorePrevious }: UseAsyncProps<TReturnType, TDefaultValueType>, deps: DependencyList) => {
17
19
  data: TReturnType | TDefaultValueType;
18
20
  error: Error | null;
19
21
  isLoading: boolean;
@@ -10,14 +10,16 @@ const react_1 = require("react");
10
10
  * 2. When any of the `deps` change.
11
11
  * 3. When the `reload` function is called.
12
12
  */
13
- const useAsync = ({ callback, defaultValue }, deps) => {
13
+ const useAsync = ({ callback, defaultValue, ignorePrevious = false }, deps) => {
14
14
  const [data, setData] = (0, react_1.useState)(defaultValue);
15
15
  const [isLoading, setIsLoading] = (0, react_1.useState)(false);
16
16
  const [error, setError] = (0, react_1.useState)(null);
17
+ const requestCountRef = (0, react_1.useRef)(0);
17
18
  const reload = (0, react_1.useCallback)(() => {
18
19
  setIsLoading(true);
19
20
  setError(null);
20
21
  setData(defaultValue);
22
+ const currentRequestCount = ignorePrevious ? ++requestCountRef.current : 0;
21
23
  try {
22
24
  const isArrayOfCallbacks = Array.isArray(callback);
23
25
  const promises = isArrayOfCallbacks ? callback.map((cb) => cb()) : [callback()];
@@ -29,12 +31,18 @@ const useAsync = ({ callback, defaultValue }, deps) => {
29
31
  }
30
32
  Promise.all(promises)
31
33
  .then((resolved) => {
32
- setData(isArrayOfCallbacks ? resolved : resolved[0]);
33
- setIsLoading(false);
34
+ // If ignorePrevious is set to TRUE we only set the new data if it's the most recent request
35
+ // and not a previous request that has taken longer than expected.
36
+ if (!ignorePrevious || currentRequestCount === requestCountRef.current) {
37
+ setData(isArrayOfCallbacks ? resolved : resolved[0]);
38
+ setIsLoading(false);
39
+ }
34
40
  })
35
41
  .catch((e) => {
36
- setError(e instanceof Error ? e : new Error(String(e)));
37
- setIsLoading(false);
42
+ if (!ignorePrevious || currentRequestCount === requestCountRef.current) {
43
+ setError(e instanceof Error ? e : new Error(String(e)));
44
+ setIsLoading(false);
45
+ }
38
46
  });
39
47
  }
40
48
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/generic-browser-lib",
3
- "version": "1.66.0",
3
+ "version": "1.67.0",
4
4
  "description": "Package with reusable components used by resource/component browsers",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -3,9 +3,10 @@ import { renderHook, waitFor } from '@testing-library/react';
3
3
  import { useAsync } from './useAsync';
4
4
 
5
5
  describe('useAsync', () => {
6
- const renderAsyncHook = (callback: () => any | [], deps: DependencyList) => {
6
+ const renderAsyncHook = (callback: () => any | [], deps: DependencyList, ignorePrevious = false) => {
7
7
  return renderHook(
8
- ({ deps }: { deps: DependencyList }) => useAsync({ callback, defaultValue: 'Initial state' }, deps),
8
+ ({ deps }: { deps: DependencyList }) =>
9
+ useAsync({ callback, defaultValue: 'Initial state', ignorePrevious }, deps),
9
10
  { initialProps: { deps } },
10
11
  );
11
12
  };
@@ -1,10 +1,12 @@
1
- import { DependencyList, useState, useCallback, useEffect } from 'react';
1
+ import { DependencyList, useState, useCallback, useEffect, useRef } from 'react';
2
2
 
3
3
  export type UseAsyncProps<TReturnType, TDefaultValueType> = {
4
4
  /** The async callback or an array of async callbacks to call for fetching data. */
5
5
  callback: (() => TReturnType | Promise<TReturnType>) | Array<() => TReturnType | Promise<TReturnType>>;
6
6
  /** The default value to populate the data as when initially mounted or reloading data. */
7
7
  defaultValue: TReturnType | TDefaultValueType;
8
+ /** Optional toggle to ignore previous async callback data */
9
+ ignorePrevious?: boolean;
8
10
  };
9
11
 
10
12
  /**
@@ -16,18 +18,21 @@ export type UseAsyncProps<TReturnType, TDefaultValueType> = {
16
18
  * 3. When the `reload` function is called.
17
19
  */
18
20
  export const useAsync = <TReturnType, TDefaultValueType>(
19
- { callback, defaultValue }: UseAsyncProps<TReturnType, TDefaultValueType>,
21
+ { callback, defaultValue, ignorePrevious = false }: UseAsyncProps<TReturnType, TDefaultValueType>,
20
22
  deps: DependencyList,
21
23
  ) => {
22
24
  const [data, setData] = useState(defaultValue);
23
25
  const [isLoading, setIsLoading] = useState(false);
24
26
  const [error, setError] = useState<Error | null>(null);
27
+ const requestCountRef = useRef(0);
25
28
 
26
29
  const reload = useCallback(() => {
27
30
  setIsLoading(true);
28
31
  setError(null);
29
32
  setData(defaultValue);
30
33
 
34
+ const currentRequestCount = ignorePrevious ? ++requestCountRef.current : 0;
35
+
31
36
  try {
32
37
  const isArrayOfCallbacks = Array.isArray(callback);
33
38
  const promises = isArrayOfCallbacks ? callback.map((cb) => cb()) : [callback()];
@@ -41,12 +46,18 @@ export const useAsync = <TReturnType, TDefaultValueType>(
41
46
 
42
47
  Promise.all(promises)
43
48
  .then((resolved: TReturnType[]) => {
44
- setData(isArrayOfCallbacks ? (resolved as TReturnType) : resolved[0]);
45
- setIsLoading(false);
49
+ // If ignorePrevious is set to TRUE we only set the new data if it's the most recent request
50
+ // and not a previous request that has taken longer than expected.
51
+ if (!ignorePrevious || currentRequestCount === requestCountRef.current) {
52
+ setData(isArrayOfCallbacks ? (resolved as TReturnType) : resolved[0]);
53
+ setIsLoading(false);
54
+ }
46
55
  })
47
56
  .catch((e: unknown) => {
48
- setError(e instanceof Error ? e : new Error(String(e)));
49
- setIsLoading(false);
57
+ if (!ignorePrevious || currentRequestCount === requestCountRef.current) {
58
+ setError(e instanceof Error ? e : new Error(String(e)));
59
+ setIsLoading(false);
60
+ }
50
61
  });
51
62
  } catch (e: unknown) {
52
63
  setError(e instanceof Error ? e : new Error(String(e)));