-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
Copy pathuseBaseQuery.ts
95 lines (83 loc) · 2.97 KB
/
useBaseQuery.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
93
94
95
import { useContext, useEffect, useReducer, useRef } from 'react';
import { DocumentNode } from 'graphql';
import {
QueryHookOptions,
QueryDataOptions,
QueryTuple,
QueryResult,
} from '../../types/types';
import { QueryData } from '../../data';
import { useDeepMemo } from './useDeepMemo';
import { OperationVariables } from '../../../core';
import { getApolloContext } from '../../context';
export function useBaseQuery<TData = any, TVariables = OperationVariables>(
query: DocumentNode,
options?: QueryHookOptions<TData, TVariables>,
lazy = false
) {
const context = useContext(getApolloContext());
const [tick, forceUpdate] = useReducer(x => x + 1, 0);
const updatedOptions = options ? { ...options, query } : { query };
const queryDataRef = useRef<QueryData<TData, TVariables>>();
const queryData =
queryDataRef.current ||
new QueryData<TData, TVariables>({
options: updatedOptions as QueryDataOptions<TData, TVariables>,
context,
onNewData() {
if (!queryData.ssrInitiated()) {
// When new data is received from the `QueryData` object, we want to
// force a re-render to make sure the new data is displayed. We can't
// force that re-render if we're already rendering however so to be
// safe we'll trigger the re-render in a microtask.
Promise.resolve().then(forceUpdate);
} else {
// If we're rendering on the server side we can force an update at
// any point.
forceUpdate();
}
}
});
queryData.setOptions(updatedOptions);
queryData.context = context;
// SSR won't trigger the effect hook below that stores the current
// `QueryData` instance for future renders, so we'll handle that here if
// the current render is happening on the server side.
if (queryData.ssrInitiated() && !queryDataRef.current) {
queryDataRef.current = queryData;
}
// `onError` and `onCompleted` callback functions will not always have a
// stable identity, so we'll exclude them from the memoization key to
// prevent `afterExecute` from being triggered un-necessarily.
const memo = {
options: {
...updatedOptions,
onError: undefined,
onCompleted: undefined
} as QueryHookOptions<TData, TVariables>,
context,
tick
};
const result = useDeepMemo(
() => (lazy ? queryData.executeLazy() : queryData.execute()),
memo
);
const queryResult = lazy
? (result as QueryTuple<TData, TVariables>)[1]
: (result as QueryResult<TData, TVariables>);
useEffect(() => {
// We only need one instance of the `QueryData` class, so we'll store it
// as a ref to make it available on subsequent renders.
if (!queryDataRef.current) {
queryDataRef.current = queryData;
}
return () => queryData.cleanup();
}, []);
useEffect(() => queryData.afterExecute({ lazy }), [
queryResult.loading,
queryResult.networkStatus,
queryResult.error,
queryResult.data,
]);
return result;
}