-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
useLazyLoadQueryNode.js
129 lines (115 loc) · 3.63 KB
/
useLazyLoadQueryNode.js
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+relay
* @flow strict-local
* @format
*/
// flowlint ambiguous-object-type:error
'use strict';
const ProfilerContext = require('./ProfilerContext');
const React = require('react');
const useFetchTrackingRef = require('./useFetchTrackingRef');
const useFragmentNode = require('./useFragmentNode');
const useRelayEnvironment = require('./useRelayEnvironment');
const {getQueryResourceForEnvironment} = require('./QueryResource');
const {
__internal: {fetchQuery},
} = require('relay-runtime');
import type {
CacheConfig,
FetchPolicy,
GraphQLResponse,
Observable,
OperationDescriptor,
OperationType,
RenderPolicy,
} from 'relay-runtime';
const {useContext, useEffect, useState, useRef} = React;
function useLazyLoadQueryNode<TQuery: OperationType>(args: {|
query: OperationDescriptor,
componentDisplayName: string,
fetchObservable?: ?Observable<GraphQLResponse>,
fetchPolicy?: ?FetchPolicy,
fetchKey?: ?string | ?number,
networkCacheConfig?: CacheConfig,
renderPolicy?: ?RenderPolicy,
|}): $ElementType<TQuery, 'response'> {
const environment = useRelayEnvironment();
const profilerContext = useContext(ProfilerContext);
const QueryResource = getQueryResourceForEnvironment(environment);
const {
query,
componentDisplayName,
fetchKey,
fetchPolicy,
renderPolicy,
} = args;
const fetchObservable =
args.fetchObservable ??
fetchQuery(environment, query, {
networkCacheConfig: args.networkCacheConfig ?? {force: true},
});
const {startFetch, completeFetch} = useFetchTrackingRef();
const preparedQueryResult = profilerContext.wrapPrepareQueryResource(() => {
return QueryResource.prepare(
query,
fetchObservable,
fetchPolicy,
renderPolicy,
{start: startFetch, complete: completeFetch, error: completeFetch},
fetchKey,
profilerContext,
);
});
let _forceUpdate;
let _maybeFastRefresh;
if (__DEV__) {
/* eslint-disable react-hooks/rules-of-hooks */
[, _forceUpdate] = useState(0);
_maybeFastRefresh = useRef(false);
useEffect(() => {
return () => {
// Detect fast refresh, only runs multiple times in fast refresh
_maybeFastRefresh.current = true;
};
}, []); // eslint-disable-line react-hooks/exhaustive-deps
/* eslint-enable react-hooks/rules-of-hooks */
}
useEffect(() => {
if (__DEV__) {
if (_maybeFastRefresh && _maybeFastRefresh.current) {
/**
* This block only runs during fast refresh, the current resource and
* it's cache is disposed in the previous cleanup. Stop retaining and
* force a re-render to restart fetchObservable and retain correctly.
*/
_maybeFastRefresh.current = false;
_forceUpdate && _forceUpdate(n => n + 1);
return;
}
}
const disposable = QueryResource.retain(
preparedQueryResult,
profilerContext,
);
return () => {
disposable.dispose();
};
// NOTE: We disable react-hooks-deps warning because the `environment`
// and `query` identities are capturing all information about whether
// the effect should be re-ran and the query re-retained.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [environment, query]);
const {fragmentNode, fragmentRef} = preparedQueryResult;
const {data} = useFragmentNode(
fragmentNode,
fragmentRef,
componentDisplayName,
);
return data;
}
module.exports = useLazyLoadQueryNode;