-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathuseMutation.ts
92 lines (73 loc) · 2.81 KB
/
useMutation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { useCallback, useContext, useEffect, useReducer, useRef } from 'react';
import { Action, QueryResponse, UseMutationResponse } from '../../client/client.types';
import { QueryError } from '../../client/errors/QueryError';
import { ClientContext } from '../../context/clientContext/clientContext';
import { RESET, RESET_LOADING, responseReducer, SET_LOADING, SET_RESPONSE } from '../../reducers/responseReducer';
import { ResponseReducer } from '../../reducers/responseReducer.types';
type ActionCreator<S, R, T> = (action: S) => Action<T, R>;
export const useMutation = <T = any, R = {}, S = any>(
actionCreator: ActionCreator<S, R, T>,
): UseMutationResponse<S, T> => {
const clientContext = useContext(ClientContext);
const isMounted = useRef(true);
const controller = useRef<AbortController | null>();
const [state, dispatch] = useReducer(responseReducer as ResponseReducer<T>, {
loading: false,
response: { error: false },
});
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
handleAbort();
};
}, []);
const handleQuery = useCallback(
async (...params: Parameters<typeof actionCreator>) => {
if (!isMounted.current) {
return { error: false } as QueryResponse<T>;
}
const action = actionCreator(...params);
const abortController = 'AbortController' in window ? new AbortController() : undefined;
const signal = action.signal || (abortController ? abortController.signal : undefined);
if (controller.current) {
controller.current.abort();
}
controller.current = abortController;
dispatch({ type: SET_LOADING });
const queryResponse = await clientContext.query<T>({ ...action, signal: action.signal || signal });
if (isMounted.current && !(queryResponse.errorObject && queryResponse.errorObject.name === 'AbortError')) {
dispatch({ type: SET_RESPONSE, response: queryResponse });
}
if (
isMounted.current &&
(queryResponse.errorObject && queryResponse.errorObject.name === 'AbortError') &&
controller.current &&
controller.current === abortController
) {
controller.current = undefined;
dispatch({ type: RESET_LOADING });
}
return queryResponse;
},
[actionCreator, clientContext.query],
);
const handleAbort = useCallback(() => {
if (controller.current) {
controller.current.abort();
}
}, []);
const handleReset = useCallback(() => {
dispatch({ type: RESET });
}, []);
if (state.response && state.response.errorObject && state.response.errorObject instanceof QueryError) {
throw state.response.errorObject;
}
return {
abort: handleAbort,
loading: state.loading,
mutate: handleQuery,
reset: handleReset,
...state.response,
};
};