1 | ;
|
2 |
|
3 | const { useCallback, useEffect, useRef } = require('react');
|
4 | const createArgErrorMessageProd = require('../private/createArgErrorMessageProd');
|
5 |
|
6 | /**
|
7 | * A React hook to create a memoized [loader]{@link Loader} from another, that
|
8 | * automatically aborts previous loading that started via this hook when new
|
9 | * loading starts via this hook, the hook arguments change, or the component
|
10 | * unmounts.
|
11 | * @kind function
|
12 | * @name useAutoAbortLoad
|
13 | * @param {Loader} load Memoized function that starts the loading.
|
14 | * @returns {Loader} Memoized function that starts the loading.
|
15 | * @example <caption>Ways to `import`.</caption>
|
16 | * ```js
|
17 | * import { useAutoAbortLoad } from 'graphql-react';
|
18 | * ```
|
19 | *
|
20 | * ```js
|
21 | * import useAutoAbortLoad from 'graphql-react/public/useAutoAbortLoad.js';
|
22 | * ```
|
23 | * @example <caption>Ways to `require`.</caption>
|
24 | * ```js
|
25 | * const { useAutoAbortLoad } = require('graphql-react');
|
26 | * ```
|
27 | *
|
28 | * ```js
|
29 | * const useAutoAbortLoad = require('graphql-react/public/useAutoAbortLoad');
|
30 | * ```
|
31 | */
|
32 | module.exports = function useAutoAbortLoad(load) {
|
33 | if (typeof load !== 'function')
|
34 | throw new TypeError(
|
35 | typeof process === 'object' && process.env.NODE_ENV !== 'production'
|
36 | ? 'Argument 1 `load` must be a function.'
|
37 | : createArgErrorMessageProd(1)
|
38 | );
|
39 |
|
40 | const lastLoadingCacheValueRef = useRef();
|
41 |
|
42 | useEffect(
|
43 | () => () => {
|
44 | if (lastLoadingCacheValueRef.current)
|
45 | // Abort the last loading as it’s now redundant due to the changed
|
46 | // dependencies. Checking if it’s already ended or aborted first is
|
47 | // unnecessary.
|
48 | lastLoadingCacheValueRef.current.abortController.abort();
|
49 | },
|
50 | [load]
|
51 | );
|
52 |
|
53 | return useCallback(() => {
|
54 | if (lastLoadingCacheValueRef.current)
|
55 | // Ensure the last loading is aborted before starting new loading.
|
56 | // Checking if it’s already ended or aborted first is unnecessary.
|
57 | lastLoadingCacheValueRef.current.abortController.abort();
|
58 |
|
59 | const loadingCacheValue = load();
|
60 |
|
61 | lastLoadingCacheValueRef.current = loadingCacheValue;
|
62 |
|
63 | // After the loading cache value promise resolves, clear the ref (if it
|
64 | // still holds the same loading cache value) to allow garbage collection.
|
65 | // This might not be worth the bundle size increase.
|
66 | loadingCacheValue.promise.then(() => {
|
67 | if (lastLoadingCacheValueRef.current === loadingCacheValue)
|
68 | lastLoadingCacheValueRef.current = null;
|
69 | });
|
70 |
|
71 | return loadingCacheValue;
|
72 | }, [load]);
|
73 | };
|