|
1 |
| -import {useEffect, useCallback, useRef, useReducer, useMemo} from 'react'; |
| 1 | +import {useEffect, useCallback, useReducer, useMemo, useState} from 'react'; |
2 | 2 | import isEqual from 'fast-deep-equal';
|
3 | 3 | import {Canceler} from 'axios';
|
4 | 4 | import {useRequest} from './useRequest';
|
@@ -49,46 +49,57 @@ export function useResource<TRequest extends Request>(
|
49 | 49 | isLoading: Boolean(defaultParams),
|
50 | 50 | });
|
51 | 51 |
|
52 |
| - const lastAppliedParams = useRef<Arguments<TRequest> | null>(null); |
| 52 | + const [requestParams, setRequestParams] = useState(defaultParams); |
53 | 53 |
|
54 | 54 | const request = useCallback(
|
55 | 55 | (...args: Arguments<TRequest> | any[]) => {
|
56 | 56 | clear(REQUEST_CLEAR_MESSAGE);
|
57 | 57 | const {ready, cancel} = createRequest(...(args as Arguments<TRequest>));
|
58 |
| - dispatch({type: 'start'}); |
59 |
| - ready() |
60 |
| - .then(data => { |
| 58 | + |
| 59 | + (async function flow() { |
| 60 | + try { |
| 61 | + dispatch({type: 'start'}); |
| 62 | + const data = await ready(); |
61 | 63 | dispatch({type: 'success', data});
|
62 |
| - }) |
63 |
| - .catch((error: RequestError) => { |
64 |
| - if (!error.isCancel) { |
65 |
| - dispatch({type: 'error', error}); |
66 |
| - } |
67 |
| - }); |
| 64 | + } catch (error) { |
| 65 | + if (!error.isCancel) dispatch({type: 'error', error}); |
| 66 | + } |
| 67 | + })(); |
| 68 | + |
68 | 69 | return cancel;
|
69 | 70 | },
|
70 | 71 | [createRequest],
|
71 | 72 | );
|
72 | 73 |
|
73 |
| - const cancel = (message?: string) => { |
74 |
| - dispatch({type: 'reset'}); |
75 |
| - clear(message); |
76 |
| - }; |
| 74 | + useEffect(() => { |
| 75 | + // The array of default request params is a dependency that we pass directly |
| 76 | + // as a dependency to this useEffect, which will run on the initial render |
| 77 | + // and subsequent params updates, triggering new requests as the params change. |
| 78 | + // If the dependency is not set, we avoid going down this road. Hooks should be |
| 79 | + // either fully controlled or self-contained. |
| 80 | + if (!defaultParams) return; |
| 81 | + |
| 82 | + // We perform an deep equality check of the params and rely on React's bail out |
| 83 | + // to control future request calls made passing default params as dependency |
| 84 | + setRequestParams(current => |
| 85 | + isEqual(current, defaultParams) ? current : defaultParams, |
| 86 | + ); |
| 87 | + }, defaultParams); |
77 | 88 |
|
78 | 89 | useEffect(() => {
|
79 |
| - let canceller: Canceler; |
80 |
| - if (defaultParams && !isEqual(defaultParams, lastAppliedParams.current)) { |
81 |
| - lastAppliedParams.current = defaultParams; |
82 |
| - canceller = request(...defaultParams); |
| 90 | + let canceller: Canceler = () => {}; |
| 91 | + if (requestParams) { |
| 92 | + canceller = request(...requestParams); |
83 | 93 | }
|
84 |
| - return () => { |
85 |
| - if (canceller) { |
86 |
| - canceller(); |
87 |
| - } |
88 |
| - }; |
89 |
| - }, defaultParams); |
| 94 | + return canceller; |
| 95 | + }, [requestParams]); |
90 | 96 |
|
91 | 97 | return useMemo(() => {
|
| 98 | + const cancel = (message?: string) => { |
| 99 | + dispatch({type: 'reset'}); |
| 100 | + clear(message); |
| 101 | + }; |
| 102 | + |
92 | 103 | const result: UseResourceResult<TRequest> = [{...state, cancel}, request];
|
93 | 104 | return result;
|
94 | 105 | }, [state, request]);
|
|
0 commit comments