react-relay 14.0.0 → 15.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/ReactRelayContainerUtils.js.flow +1 -2
- package/ReactRelayContext.js +1 -1
- package/ReactRelayContext.js.flow +1 -2
- package/ReactRelayFragmentContainer.js.flow +6 -4
- package/ReactRelayFragmentMockRenderer.js.flow +1 -2
- package/ReactRelayLocalQueryRenderer.js.flow +5 -5
- package/ReactRelayPaginationContainer.js.flow +21 -14
- package/ReactRelayQueryFetcher.js.flow +28 -16
- package/ReactRelayQueryRenderer.js.flow +42 -13
- package/ReactRelayQueryRendererContext.js.flow +2 -3
- package/ReactRelayRefetchContainer.js.flow +9 -9
- package/ReactRelayTestMocker.js.flow +3 -3
- package/ReactRelayTypes.js.flow +7 -8
- package/RelayContext.js.flow +1 -2
- package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +4 -5
- package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +4 -5
- package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +4 -5
- package/__flowtests__/RelayModern-flowtest.js.flow +3 -4
- package/__flowtests__/RelayModernFlowtest_badref.graphql.js.flow +3 -4
- package/__flowtests__/RelayModernFlowtest_notref.graphql.js.flow +3 -4
- package/__flowtests__/RelayModernFlowtest_user.graphql.js.flow +3 -4
- package/__flowtests__/RelayModernFlowtest_users.graphql.js.flow +3 -4
- package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +3 -1
- package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +3 -1
- package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +4 -2
- package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +3 -1
- package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +4 -2
- package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +3 -1
- package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +4 -2
- package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +4 -2
- package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +3 -1
- package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +3 -1
- package/assertFragmentMap.js.flow +1 -2
- package/buildReactRelayContainer.js.flow +7 -7
- package/getRootVariablesForFragments.js.flow +1 -3
- package/hooks.js +1 -1
- package/hooks.js.flow +4 -2
- package/index.js +1 -1
- package/index.js.flow +6 -2
- package/isRelayEnvironment.js.flow +1 -2
- package/jest-react/enqueueTask.js.flow +1 -1
- package/jest-react/index.js.flow +1 -1
- package/jest-react/internalAct.js.flow +1 -1
- package/legacy.js +1 -1
- package/legacy.js.flow +1 -2
- package/lib/ReactRelayContainerUtils.js +2 -3
- package/lib/ReactRelayContext.js +3 -4
- package/lib/ReactRelayFragmentContainer.js +47 -73
- package/lib/ReactRelayFragmentMockRenderer.js +2 -4
- package/lib/ReactRelayLocalQueryRenderer.js +18 -31
- package/lib/ReactRelayPaginationContainer.js +74 -164
- package/lib/ReactRelayQueryFetcher.js +49 -76
- package/lib/ReactRelayQueryRenderer.js +63 -84
- package/lib/ReactRelayQueryRendererContext.js +2 -2
- package/lib/ReactRelayRefetchContainer.js +58 -108
- package/lib/ReactRelayTestMocker.js +33 -68
- package/lib/ReactRelayTypes.js +2 -1
- package/lib/RelayContext.js +4 -7
- package/lib/assertFragmentMap.js +3 -5
- package/lib/buildReactRelayContainer.js +11 -27
- package/lib/getRootVariablesForFragments.js +6 -10
- package/lib/hooks.js +5 -18
- package/lib/index.js +7 -24
- package/lib/isRelayEnvironment.js +5 -4
- package/lib/jest-react/enqueueTask.js +5 -9
- package/lib/jest-react/index.js +0 -1
- package/lib/jest-react/internalAct.js +9 -20
- package/lib/legacy.js +2 -8
- package/lib/multi-actor/ActorChange.js +2 -5
- package/lib/multi-actor/index.js +2 -1
- package/lib/multi-actor/useRelayActorEnvironment.js +4 -8
- package/lib/relay-hooks/EntryPointContainer.react.js +9 -15
- package/lib/relay-hooks/EntryPointTypes.flow.js +5 -3
- package/lib/relay-hooks/FragmentResource.js +109 -203
- package/lib/relay-hooks/HooksImplementation.js +3 -6
- package/lib/relay-hooks/InternalLogger.js +2 -3
- package/lib/relay-hooks/LRUCache.js +2 -20
- package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +33 -54
- package/lib/relay-hooks/MatchContainer.js +15 -24
- package/lib/relay-hooks/ProfilerContext.js +3 -3
- package/lib/relay-hooks/QueryResource.js +31 -101
- package/lib/relay-hooks/RelayEnvironmentProvider.js +5 -9
- package/lib/relay-hooks/SuspenseResource.js +9 -33
- package/lib/relay-hooks/loadEntryPoint.js +19 -31
- package/lib/relay-hooks/loadQuery.js +42 -78
- package/lib/relay-hooks/preloadQuery_DEPRECATED.js +11 -37
- package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +9 -15
- package/lib/relay-hooks/react-cache/RelayReactCache.js +7 -12
- package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +27 -81
- package/lib/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js +206 -0
- package/lib/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js +195 -215
- package/lib/relay-hooks/react-cache/useFragment_REACT_CACHE.js +5 -15
- package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +17 -24
- package/lib/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js +149 -0
- package/lib/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js +24 -39
- package/lib/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js +325 -0
- package/lib/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js +37 -0
- package/lib/relay-hooks/useBlockingPaginationFragment.js +73 -93
- package/lib/relay-hooks/useClientQuery.js +30 -0
- package/lib/relay-hooks/useEntryPointLoader.js +18 -38
- package/lib/relay-hooks/useFetchTrackingRef.js +10 -12
- package/lib/relay-hooks/useFragment.js +8 -19
- package/lib/relay-hooks/useFragmentNode.js +20 -32
- package/lib/relay-hooks/useIsMountedRef.js +4 -6
- package/lib/relay-hooks/useIsOperationNodeActive.js +8 -20
- package/lib/relay-hooks/useIsParentQueryActive.js +3 -6
- package/lib/relay-hooks/useLazyLoadQuery.js +7 -24
- package/lib/relay-hooks/useLazyLoadQueryNode.js +23 -34
- package/lib/relay-hooks/useLoadMoreFunction.js +46 -78
- package/lib/relay-hooks/useMemoOperationDescriptor.js +6 -15
- package/lib/relay-hooks/useMemoVariables.js +15 -34
- package/lib/relay-hooks/useMutation.js +9 -27
- package/lib/relay-hooks/usePaginationFragment.js +73 -76
- package/lib/relay-hooks/usePreloadedQuery.js +13 -44
- package/lib/relay-hooks/useQueryLoader.js +24 -49
- package/lib/relay-hooks/useRefetchableFragment.js +19 -17
- package/lib/relay-hooks/useRefetchableFragmentNode.js +65 -109
- package/lib/relay-hooks/useRelayEnvironment.js +4 -8
- package/lib/relay-hooks/useStaticFragmentNodeWarning.js +4 -8
- package/lib/relay-hooks/useSubscribeToInvalidationState.js +8 -9
- package/lib/relay-hooks/useSubscription.js +5 -10
- package/lib/relay-hooks/useUnsafeRef_DEPRECATED.js +29 -0
- package/multi-actor/ActorChange.js.flow +1 -1
- package/multi-actor/index.js.flow +1 -1
- package/multi-actor/useRelayActorEnvironment.js.flow +2 -4
- package/package.json +2 -2
- package/react-relay-hooks.js +2 -2
- package/react-relay-hooks.min.js +2 -2
- package/react-relay-legacy.js +2 -2
- package/react-relay-legacy.min.js +2 -2
- package/react-relay.js +2 -2
- package/react-relay.min.js +2 -2
- package/relay-hooks/EntryPointContainer.react.js.flow +3 -5
- package/relay-hooks/EntryPointTypes.flow.js.flow +37 -37
- package/relay-hooks/FragmentResource.js.flow +43 -19
- package/relay-hooks/HooksImplementation.js.flow +7 -9
- package/relay-hooks/InternalLogger.js.flow +1 -3
- package/relay-hooks/LRUCache.js.flow +1 -3
- package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +19 -14
- package/relay-hooks/MatchContainer.js.flow +6 -8
- package/relay-hooks/ProfilerContext.js.flow +1 -3
- package/relay-hooks/QueryResource.js.flow +29 -11
- package/relay-hooks/RelayEnvironmentProvider.js.flow +4 -6
- package/relay-hooks/SuspenseResource.js.flow +1 -3
- package/relay-hooks/__flowtests__/EntryPointTypes/EntryPointElementConfig-flowtest.js.flow +6 -4
- package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +4 -4
- package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +3 -1
- package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +3 -1
- package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +39 -39
- package/relay-hooks/__flowtests__/useFragment-flowtest.js.flow +1 -3
- package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +37 -38
- package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +18 -20
- package/relay-hooks/__flowtests__/utils.js.flow +21 -12
- package/relay-hooks/loadEntryPoint.js.flow +11 -6
- package/relay-hooks/loadQuery.js.flow +11 -7
- package/relay-hooks/preloadQuery_DEPRECATED.js.flow +9 -12
- package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +13 -10
- package/relay-hooks/react-cache/RelayReactCache.js.flow +1 -3
- package/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js.flow +26 -20
- package/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js.flow +297 -0
- package/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js.flow +136 -96
- package/relay-hooks/react-cache/useFragment_REACT_CACHE.js.flow +1 -3
- package/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js.flow +3 -5
- package/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js.flow +190 -0
- package/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js.flow +3 -6
- package/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js.flow +601 -0
- package/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js.flow +65 -0
- package/relay-hooks/useBlockingPaginationFragment.js.flow +86 -59
- package/relay-hooks/useClientQuery.js.flow +39 -0
- package/relay-hooks/useEntryPointLoader.js.flow +16 -14
- package/relay-hooks/useFetchTrackingRef.js.flow +7 -8
- package/relay-hooks/useFragment.js.flow +2 -4
- package/relay-hooks/useFragmentNode.js.flow +7 -8
- package/relay-hooks/useIsMountedRef.js.flow +2 -4
- package/relay-hooks/useIsOperationNodeActive.js.flow +1 -1
- package/relay-hooks/useIsParentQueryActive.js.flow +1 -1
- package/relay-hooks/useLazyLoadQuery.js.flow +9 -32
- package/relay-hooks/useLazyLoadQueryNode.js.flow +4 -6
- package/relay-hooks/useLoadMoreFunction.js.flow +20 -17
- package/relay-hooks/useMemoOperationDescriptor.js.flow +3 -5
- package/relay-hooks/useMemoVariables.js.flow +13 -31
- package/relay-hooks/useMutation.js.flow +6 -8
- package/relay-hooks/usePaginationFragment.js.flow +75 -43
- package/relay-hooks/usePreloadedQuery.js.flow +49 -43
- package/relay-hooks/useQueryLoader.js.flow +89 -28
- package/relay-hooks/useRefetchableFragment.js.flow +83 -23
- package/relay-hooks/useRefetchableFragmentNode.js.flow +26 -22
- package/relay-hooks/useRelayEnvironment.js.flow +2 -4
- package/relay-hooks/useStaticFragmentNodeWarning.js.flow +3 -5
- package/relay-hooks/useSubscribeToInvalidationState.js.flow +2 -4
- package/relay-hooks/useSubscription.js.flow +1 -3
- package/relay-hooks/useUnsafeRef_DEPRECATED.js.flow +25 -0
- package/lib/readContext.js +0 -28
- package/readContext.js.flow +0 -31
@@ -0,0 +1,601 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*
|
7
|
+
* @flow strict-local
|
8
|
+
* @format
|
9
|
+
* @oncall relay
|
10
|
+
*/
|
11
|
+
|
12
|
+
'use strict';
|
13
|
+
|
14
|
+
import type {LoaderFn} from '../useQueryLoader';
|
15
|
+
import type {
|
16
|
+
ConcreteRequest,
|
17
|
+
Disposable,
|
18
|
+
FetchPolicy,
|
19
|
+
IEnvironment,
|
20
|
+
OperationDescriptor,
|
21
|
+
OperationType,
|
22
|
+
ReaderFragment,
|
23
|
+
RenderPolicy,
|
24
|
+
Variables,
|
25
|
+
VariablesOf,
|
26
|
+
} from 'relay-runtime';
|
27
|
+
|
28
|
+
const ProfilerContext = require('../ProfilerContext');
|
29
|
+
const {getQueryResourceForEnvironment} = require('../QueryResource');
|
30
|
+
const useIsMountedRef = require('../useIsMountedRef');
|
31
|
+
const useQueryLoader = require('../useQueryLoader');
|
32
|
+
const useRelayEnvironment = require('../useRelayEnvironment');
|
33
|
+
const readFragmentInternal = require('./readFragmentInternal_REACT_CACHE');
|
34
|
+
const useFragmentInternal = require('./useFragmentInternal_REACT_CACHE');
|
35
|
+
const invariant = require('invariant');
|
36
|
+
const {useCallback, useContext, useReducer} = require('react');
|
37
|
+
const {
|
38
|
+
__internal: {fetchQuery},
|
39
|
+
createOperationDescriptor,
|
40
|
+
getFragmentIdentifier,
|
41
|
+
getRefetchMetadata,
|
42
|
+
getSelector,
|
43
|
+
getValueAtPath,
|
44
|
+
} = require('relay-runtime');
|
45
|
+
const warning = require('warning');
|
46
|
+
|
47
|
+
export type RefetchFn<
|
48
|
+
TQuery: OperationType,
|
49
|
+
TOptions = Options,
|
50
|
+
> = RefetchFnExact<TQuery, TOptions>;
|
51
|
+
|
52
|
+
// NOTE: RefetchFnDynamic returns a refetch function that:
|
53
|
+
// - Expects the /exact/ set of query variables if the provided key type is
|
54
|
+
// /nullable/.
|
55
|
+
// - Or, expects /a subset/ of the query variables if the provided key type is
|
56
|
+
// /non-null/.
|
57
|
+
// prettier-ignore
|
58
|
+
export type RefetchFnDynamic<
|
59
|
+
TQuery: OperationType,
|
60
|
+
TKey: ?{ +$data?: mixed, ... },
|
61
|
+
TOptions = Options,
|
62
|
+
> = $Call<
|
63
|
+
& (( { +$data?: mixed, ... }) => RefetchFnInexact<TQuery, TOptions>)
|
64
|
+
& ((?{ +$data?: mixed, ... }) => RefetchFnExact<TQuery, TOptions>),
|
65
|
+
TKey
|
66
|
+
>;
|
67
|
+
|
68
|
+
export type ReturnType<
|
69
|
+
TQuery: OperationType,
|
70
|
+
TKey: ?{+$data?: mixed, ...},
|
71
|
+
TOptions = Options,
|
72
|
+
> = {
|
73
|
+
fragmentData: mixed,
|
74
|
+
fragmentRef: mixed,
|
75
|
+
refetch: RefetchFnDynamic<TQuery, TKey, TOptions>,
|
76
|
+
};
|
77
|
+
|
78
|
+
export type Options = {
|
79
|
+
fetchPolicy?: FetchPolicy,
|
80
|
+
onComplete?: (Error | null) => void,
|
81
|
+
UNSTABLE_renderPolicy?: RenderPolicy,
|
82
|
+
};
|
83
|
+
|
84
|
+
type InternalOptions = {
|
85
|
+
...Options,
|
86
|
+
__environment?: IEnvironment,
|
87
|
+
};
|
88
|
+
|
89
|
+
type RefetchFnBase<TVars, TOptions> = (
|
90
|
+
vars: TVars,
|
91
|
+
options?: TOptions,
|
92
|
+
) => Disposable;
|
93
|
+
|
94
|
+
type RefetchFnExact<TQuery: OperationType, TOptions = Options> = RefetchFnBase<
|
95
|
+
VariablesOf<TQuery>,
|
96
|
+
TOptions,
|
97
|
+
>;
|
98
|
+
type RefetchFnInexact<
|
99
|
+
TQuery: OperationType,
|
100
|
+
TOptions = Options,
|
101
|
+
> = RefetchFnBase<Partial<VariablesOf<TQuery>>, TOptions>;
|
102
|
+
|
103
|
+
type Action =
|
104
|
+
| {
|
105
|
+
type: 'reset',
|
106
|
+
environment: IEnvironment,
|
107
|
+
fragmentIdentifier: string,
|
108
|
+
}
|
109
|
+
| {
|
110
|
+
type: 'refetch',
|
111
|
+
refetchQuery: OperationDescriptor,
|
112
|
+
fetchPolicy?: FetchPolicy,
|
113
|
+
renderPolicy?: RenderPolicy,
|
114
|
+
onComplete?: (Error | null) => void,
|
115
|
+
refetchEnvironment: ?IEnvironment,
|
116
|
+
};
|
117
|
+
|
118
|
+
type RefetchState = {
|
119
|
+
fetchPolicy: FetchPolicy | void,
|
120
|
+
mirroredEnvironment: IEnvironment,
|
121
|
+
mirroredFragmentIdentifier: string,
|
122
|
+
onComplete: ((Error | null) => void) | void,
|
123
|
+
refetchEnvironment?: ?IEnvironment,
|
124
|
+
refetchQuery: OperationDescriptor | null,
|
125
|
+
renderPolicy: RenderPolicy | void,
|
126
|
+
};
|
127
|
+
|
128
|
+
type DebugIDandTypename = {
|
129
|
+
id: string,
|
130
|
+
typename: string,
|
131
|
+
...
|
132
|
+
};
|
133
|
+
|
134
|
+
function reducer(state: RefetchState, action: Action): RefetchState {
|
135
|
+
switch (action.type) {
|
136
|
+
case 'refetch': {
|
137
|
+
return {
|
138
|
+
...state,
|
139
|
+
fetchPolicy: action.fetchPolicy,
|
140
|
+
mirroredEnvironment:
|
141
|
+
action.refetchEnvironment ?? state.mirroredEnvironment,
|
142
|
+
onComplete: action.onComplete,
|
143
|
+
refetchEnvironment: action.refetchEnvironment,
|
144
|
+
refetchQuery: action.refetchQuery,
|
145
|
+
renderPolicy: action.renderPolicy,
|
146
|
+
};
|
147
|
+
}
|
148
|
+
case 'reset': {
|
149
|
+
return {
|
150
|
+
fetchPolicy: undefined,
|
151
|
+
mirroredEnvironment: action.environment,
|
152
|
+
mirroredFragmentIdentifier: action.fragmentIdentifier,
|
153
|
+
onComplete: undefined,
|
154
|
+
refetchQuery: null,
|
155
|
+
renderPolicy: undefined,
|
156
|
+
};
|
157
|
+
}
|
158
|
+
default: {
|
159
|
+
(action.type: empty);
|
160
|
+
throw new Error('useRefetchableFragmentNode: Unexpected action type');
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
function useRefetchableFragmentNode<
|
166
|
+
TQuery: OperationType,
|
167
|
+
TKey: ?{+$data?: mixed, ...},
|
168
|
+
>(
|
169
|
+
fragmentNode: ReaderFragment,
|
170
|
+
parentFragmentRef: mixed,
|
171
|
+
componentDisplayName: string,
|
172
|
+
): ReturnType<TQuery, TKey, InternalOptions> {
|
173
|
+
const parentEnvironment = useRelayEnvironment();
|
174
|
+
const {refetchableRequest, fragmentRefPathInResponse, identifierField} =
|
175
|
+
getRefetchMetadata(fragmentNode, componentDisplayName);
|
176
|
+
const fragmentIdentifier = getFragmentIdentifier(
|
177
|
+
fragmentNode,
|
178
|
+
parentFragmentRef,
|
179
|
+
);
|
180
|
+
|
181
|
+
const [refetchState, dispatch] = useReducer(reducer, {
|
182
|
+
fetchPolicy: undefined,
|
183
|
+
mirroredEnvironment: parentEnvironment,
|
184
|
+
mirroredFragmentIdentifier: fragmentIdentifier,
|
185
|
+
onComplete: undefined,
|
186
|
+
refetchEnvironment: null,
|
187
|
+
refetchQuery: null,
|
188
|
+
renderPolicy: undefined,
|
189
|
+
});
|
190
|
+
const {
|
191
|
+
fetchPolicy,
|
192
|
+
mirroredEnvironment,
|
193
|
+
mirroredFragmentIdentifier,
|
194
|
+
onComplete,
|
195
|
+
refetchEnvironment,
|
196
|
+
refetchQuery,
|
197
|
+
renderPolicy,
|
198
|
+
} = refetchState;
|
199
|
+
const environment = refetchEnvironment ?? parentEnvironment;
|
200
|
+
|
201
|
+
const QueryResource = getQueryResourceForEnvironment(environment);
|
202
|
+
const profilerContext = useContext(ProfilerContext);
|
203
|
+
|
204
|
+
const shouldReset =
|
205
|
+
environment !== mirroredEnvironment ||
|
206
|
+
fragmentIdentifier !== mirroredFragmentIdentifier;
|
207
|
+
const [queryRef, loadQuery, disposeQuery] = useQueryLoader<
|
208
|
+
TQuery['variables'],
|
209
|
+
TQuery['response'],
|
210
|
+
TQuery['rawResponse'],
|
211
|
+
>((refetchableRequest: $FlowFixMe));
|
212
|
+
|
213
|
+
let fragmentRef = parentFragmentRef;
|
214
|
+
if (shouldReset) {
|
215
|
+
dispatch({
|
216
|
+
type: 'reset',
|
217
|
+
environment,
|
218
|
+
fragmentIdentifier,
|
219
|
+
});
|
220
|
+
disposeQuery();
|
221
|
+
} else if (refetchQuery != null && queryRef != null) {
|
222
|
+
// If refetch was called, we expect to have a refetchQuery and queryRef
|
223
|
+
// in state, since both state updates to set the refetchQuery and the
|
224
|
+
// queryRef occur simultaneously.
|
225
|
+
// In this case, we need to read the refetched query data (potentially
|
226
|
+
// suspending if it's in flight), and extract the new fragment ref
|
227
|
+
// from the query in order read the current @refetchable fragment
|
228
|
+
// with the updated fragment owner as the new refetchQuery.
|
229
|
+
|
230
|
+
// Before observing the refetch, record the current ID and typename
|
231
|
+
// so that, if we are refetching existing data on
|
232
|
+
// a field that implements Node, after refetching we
|
233
|
+
// can validate that the received data is consistent
|
234
|
+
let debugPreviousIDAndTypename: ?DebugIDandTypename;
|
235
|
+
if (__DEV__) {
|
236
|
+
debugPreviousIDAndTypename = debugFunctions.getInitialIDAndType(
|
237
|
+
refetchQuery.request.variables,
|
238
|
+
fragmentRefPathInResponse,
|
239
|
+
environment,
|
240
|
+
);
|
241
|
+
}
|
242
|
+
|
243
|
+
const handleQueryCompleted = (maybeError: void | Error) => {
|
244
|
+
onComplete && onComplete(maybeError ?? null);
|
245
|
+
};
|
246
|
+
|
247
|
+
// The queryRef.source obtained from useQueryLoader will be
|
248
|
+
// an observable we can consume /if/ a network request was
|
249
|
+
// started. Otherwise, given that QueryResource.prepare
|
250
|
+
// always expects an observable we fall back to a new network
|
251
|
+
// observable. Note however that if loadQuery did not make a network
|
252
|
+
// request, we don't expect to make one here, unless the state of
|
253
|
+
// the cache has changed between the call to refetch and this
|
254
|
+
// render.
|
255
|
+
const fetchObservable =
|
256
|
+
queryRef.source != null
|
257
|
+
? queryRef.source
|
258
|
+
: fetchQuery(environment, refetchQuery);
|
259
|
+
|
260
|
+
// Now wwe can we read the refetch query here using the
|
261
|
+
// queryRef provided from useQueryLoader. Note that the
|
262
|
+
// network request is started during the call to refetch,
|
263
|
+
// but if the refetch query is still in flight, we will suspend
|
264
|
+
// at this point:
|
265
|
+
const queryResult = profilerContext.wrapPrepareQueryResource(() => {
|
266
|
+
return QueryResource.prepare(
|
267
|
+
refetchQuery,
|
268
|
+
fetchObservable,
|
269
|
+
fetchPolicy,
|
270
|
+
renderPolicy,
|
271
|
+
{
|
272
|
+
error: handleQueryCompleted,
|
273
|
+
complete: () => {
|
274
|
+
// Validate that the type of the object we got back matches the type
|
275
|
+
// of the object already in the store
|
276
|
+
if (__DEV__) {
|
277
|
+
debugFunctions.checkSameTypeAfterRefetch(
|
278
|
+
debugPreviousIDAndTypename,
|
279
|
+
environment,
|
280
|
+
fragmentNode,
|
281
|
+
componentDisplayName,
|
282
|
+
);
|
283
|
+
}
|
284
|
+
handleQueryCompleted();
|
285
|
+
},
|
286
|
+
},
|
287
|
+
queryRef.fetchKey,
|
288
|
+
profilerContext,
|
289
|
+
);
|
290
|
+
});
|
291
|
+
|
292
|
+
const queryData = readFragmentInternal(
|
293
|
+
environment,
|
294
|
+
queryResult.fragmentNode,
|
295
|
+
queryResult.fragmentRef,
|
296
|
+
componentDisplayName,
|
297
|
+
).data;
|
298
|
+
invariant(
|
299
|
+
queryData != null,
|
300
|
+
'Relay: Expected to be able to read refetch query response. ' +
|
301
|
+
"If you're seeing this, this is likely a bug in Relay.",
|
302
|
+
);
|
303
|
+
|
304
|
+
// After reading/fetching the refetch query, we extract from the
|
305
|
+
// refetch query response the new fragment ref we need to use to read
|
306
|
+
// the fragment. The new fragment ref will point to the refetch query
|
307
|
+
// as its fragment owner.
|
308
|
+
const refetchedFragmentRef = getValueAtPath(
|
309
|
+
queryData,
|
310
|
+
fragmentRefPathInResponse,
|
311
|
+
);
|
312
|
+
fragmentRef = refetchedFragmentRef;
|
313
|
+
|
314
|
+
if (__DEV__) {
|
315
|
+
// Validate that the id of the object we got back matches the id
|
316
|
+
// we queried for in the variables.
|
317
|
+
// We do this during render instead of onComplete to make sure we are
|
318
|
+
// only validating the most recent refetch.
|
319
|
+
debugFunctions.checkSameIDAfterRefetch(
|
320
|
+
debugPreviousIDAndTypename,
|
321
|
+
fragmentRef,
|
322
|
+
fragmentNode,
|
323
|
+
componentDisplayName,
|
324
|
+
);
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
// We read and subscribe to the fragment using useFragmentNode.
|
329
|
+
// If refetch was called, we read the fragment using the new computed
|
330
|
+
// fragment ref from the refetch query response; otherwise, we use the
|
331
|
+
// fragment ref passed by the caller as normal.
|
332
|
+
const fragmentData = useFragmentInternal(
|
333
|
+
fragmentNode,
|
334
|
+
fragmentRef,
|
335
|
+
componentDisplayName,
|
336
|
+
);
|
337
|
+
|
338
|
+
const refetch = useRefetchFunction<TQuery>(
|
339
|
+
componentDisplayName,
|
340
|
+
dispatch,
|
341
|
+
disposeQuery,
|
342
|
+
fragmentData,
|
343
|
+
fragmentIdentifier,
|
344
|
+
fragmentNode,
|
345
|
+
fragmentRefPathInResponse,
|
346
|
+
identifierField,
|
347
|
+
loadQuery,
|
348
|
+
parentFragmentRef,
|
349
|
+
refetchableRequest,
|
350
|
+
);
|
351
|
+
return {
|
352
|
+
fragmentData,
|
353
|
+
fragmentRef,
|
354
|
+
refetch,
|
355
|
+
};
|
356
|
+
}
|
357
|
+
|
358
|
+
function useRefetchFunction<TQuery: OperationType>(
|
359
|
+
componentDisplayName: string,
|
360
|
+
dispatch: (
|
361
|
+
| {
|
362
|
+
environment: IEnvironment,
|
363
|
+
fragmentIdentifier: string,
|
364
|
+
type: 'reset',
|
365
|
+
}
|
366
|
+
| {
|
367
|
+
fetchPolicy?: FetchPolicy,
|
368
|
+
onComplete?: (Error | null) => void,
|
369
|
+
refetchEnvironment: ?IEnvironment,
|
370
|
+
refetchQuery: OperationDescriptor,
|
371
|
+
renderPolicy?: RenderPolicy,
|
372
|
+
type: 'refetch',
|
373
|
+
},
|
374
|
+
) => void,
|
375
|
+
disposeQuery: () => void,
|
376
|
+
fragmentData: mixed,
|
377
|
+
fragmentIdentifier: string,
|
378
|
+
fragmentNode: ReaderFragment,
|
379
|
+
fragmentRefPathInResponse: $ReadOnlyArray<string | number>,
|
380
|
+
identifierField: ?string,
|
381
|
+
loadQuery: LoaderFn<TQuery>,
|
382
|
+
parentFragmentRef: mixed,
|
383
|
+
refetchableRequest: ConcreteRequest,
|
384
|
+
): RefetchFn<TQuery, InternalOptions> {
|
385
|
+
const isMountedRef = useIsMountedRef();
|
386
|
+
const identifierValue =
|
387
|
+
identifierField != null &&
|
388
|
+
fragmentData != null &&
|
389
|
+
typeof fragmentData === 'object'
|
390
|
+
? fragmentData[identifierField]
|
391
|
+
: null;
|
392
|
+
return useCallback(
|
393
|
+
(
|
394
|
+
providedRefetchVariables: VariablesOf<TQuery>,
|
395
|
+
options: void | InternalOptions,
|
396
|
+
) => {
|
397
|
+
// Bail out and warn if we're trying to refetch after the component
|
398
|
+
// has unmounted
|
399
|
+
if (isMountedRef.current !== true) {
|
400
|
+
warning(
|
401
|
+
false,
|
402
|
+
'Relay: Unexpected call to `refetch` on unmounted component for fragment ' +
|
403
|
+
'`%s` in `%s`. It looks like some instances of your component are ' +
|
404
|
+
'still trying to fetch data but they already unmounted. ' +
|
405
|
+
'Please make sure you clear all timers, intervals, ' +
|
406
|
+
'async calls, etc that may trigger a fetch.',
|
407
|
+
fragmentNode.name,
|
408
|
+
componentDisplayName,
|
409
|
+
);
|
410
|
+
return {dispose: () => {}};
|
411
|
+
}
|
412
|
+
if (parentFragmentRef == null) {
|
413
|
+
warning(
|
414
|
+
false,
|
415
|
+
'Relay: Unexpected call to `refetch` while using a null fragment ref ' +
|
416
|
+
'for fragment `%s` in `%s`. When calling `refetch`, we expect ' +
|
417
|
+
"initial fragment data to be non-null. Please make sure you're " +
|
418
|
+
'passing a valid fragment ref to `%s` before calling ' +
|
419
|
+
'`refetch`, or make sure you pass all required variables to `refetch`.',
|
420
|
+
fragmentNode.name,
|
421
|
+
componentDisplayName,
|
422
|
+
componentDisplayName,
|
423
|
+
);
|
424
|
+
}
|
425
|
+
|
426
|
+
const refetchEnvironment = options?.__environment;
|
427
|
+
const fetchPolicy = options?.fetchPolicy;
|
428
|
+
const renderPolicy = options?.UNSTABLE_renderPolicy;
|
429
|
+
const onComplete = options?.onComplete;
|
430
|
+
const fragmentSelector = getSelector(fragmentNode, parentFragmentRef);
|
431
|
+
let parentVariables: Variables;
|
432
|
+
let fragmentVariables: Variables;
|
433
|
+
if (fragmentSelector == null) {
|
434
|
+
parentVariables = {};
|
435
|
+
fragmentVariables = {};
|
436
|
+
} else if (fragmentSelector.kind === 'PluralReaderSelector') {
|
437
|
+
parentVariables = fragmentSelector.selectors[0]?.owner.variables ?? {};
|
438
|
+
fragmentVariables = fragmentSelector.selectors[0]?.variables ?? {};
|
439
|
+
} else {
|
440
|
+
parentVariables = fragmentSelector.owner.variables;
|
441
|
+
fragmentVariables = fragmentSelector.variables;
|
442
|
+
}
|
443
|
+
|
444
|
+
// A user of `useRefetchableFragment()` may pass a subset of
|
445
|
+
// all variables required by the fragment when calling `refetch()`.
|
446
|
+
// We fill in any variables not passed by the call to `refetch()` with the
|
447
|
+
// variables from the original parent fragment owner.
|
448
|
+
const refetchVariables: VariablesOf<TQuery> = {
|
449
|
+
...(parentVariables: $FlowFixMe),
|
450
|
+
...fragmentVariables,
|
451
|
+
...providedRefetchVariables,
|
452
|
+
};
|
453
|
+
|
454
|
+
// If the query needs an identifier value ('id' or similar) and one
|
455
|
+
// was not explicitly provided, read it from the fragment data.
|
456
|
+
if (
|
457
|
+
identifierField != null &&
|
458
|
+
!providedRefetchVariables.hasOwnProperty('id')
|
459
|
+
) {
|
460
|
+
// @refetchable fragments are guaranteed to have an `id` selection
|
461
|
+
// if the type is Node, implements Node, or is @fetchable. Double-check
|
462
|
+
// that there actually is a value at runtime.
|
463
|
+
if (typeof identifierValue !== 'string') {
|
464
|
+
warning(
|
465
|
+
false,
|
466
|
+
'Relay: Expected result to have a string ' +
|
467
|
+
'`%s` in order to refetch, got `%s`.',
|
468
|
+
identifierField,
|
469
|
+
identifierValue,
|
470
|
+
);
|
471
|
+
}
|
472
|
+
(refetchVariables: $FlowFixMe).id = identifierValue;
|
473
|
+
}
|
474
|
+
|
475
|
+
const refetchQuery = createOperationDescriptor(
|
476
|
+
refetchableRequest,
|
477
|
+
refetchVariables,
|
478
|
+
{force: true},
|
479
|
+
);
|
480
|
+
|
481
|
+
// We call loadQuery which will start a network request if necessary
|
482
|
+
// and update the querRef from useQueryLoader.
|
483
|
+
// Note the following:
|
484
|
+
// - loadQuery will dispose of any previously refetched queries.
|
485
|
+
// - We use the variables extracted off the OperationDescriptor
|
486
|
+
// so that they have been filtered out to include only the
|
487
|
+
// variables actually declared in the query.
|
488
|
+
loadQuery(refetchQuery.request.variables, {
|
489
|
+
fetchPolicy,
|
490
|
+
__environment: refetchEnvironment,
|
491
|
+
__nameForWarning: 'refetch',
|
492
|
+
});
|
493
|
+
|
494
|
+
dispatch({
|
495
|
+
type: 'refetch',
|
496
|
+
fetchPolicy,
|
497
|
+
onComplete,
|
498
|
+
refetchEnvironment,
|
499
|
+
refetchQuery,
|
500
|
+
renderPolicy,
|
501
|
+
});
|
502
|
+
return {dispose: disposeQuery};
|
503
|
+
},
|
504
|
+
// NOTE: We disable react-hooks-deps warning because:
|
505
|
+
// - We know fragmentRefPathInResponse is static, so it can be omitted from
|
506
|
+
// deps
|
507
|
+
// - We know fragmentNode is static, so it can be omitted from deps.
|
508
|
+
// - fragmentNode and parentFragmentRef are also captured by including
|
509
|
+
// fragmentIdentifier
|
510
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
511
|
+
[fragmentIdentifier, dispatch, disposeQuery, identifierValue, loadQuery],
|
512
|
+
);
|
513
|
+
}
|
514
|
+
|
515
|
+
let debugFunctions;
|
516
|
+
if (__DEV__) {
|
517
|
+
debugFunctions = {
|
518
|
+
getInitialIDAndType(
|
519
|
+
memoRefetchVariables: ?Variables,
|
520
|
+
fragmentRefPathInResponse: $ReadOnlyArray<string | number>,
|
521
|
+
environment: IEnvironment,
|
522
|
+
): ?DebugIDandTypename {
|
523
|
+
const {Record} = require('relay-runtime');
|
524
|
+
const id = memoRefetchVariables?.id;
|
525
|
+
if (
|
526
|
+
fragmentRefPathInResponse.length !== 1 ||
|
527
|
+
fragmentRefPathInResponse[0] !== 'node' ||
|
528
|
+
id == null
|
529
|
+
) {
|
530
|
+
return null;
|
531
|
+
}
|
532
|
+
const recordSource = environment.getStore().getSource();
|
533
|
+
const record = recordSource.get(id);
|
534
|
+
const typename = record && Record.getType(record);
|
535
|
+
if (typename == null) {
|
536
|
+
return null;
|
537
|
+
}
|
538
|
+
return {
|
539
|
+
id,
|
540
|
+
typename,
|
541
|
+
};
|
542
|
+
},
|
543
|
+
|
544
|
+
checkSameTypeAfterRefetch(
|
545
|
+
previousIDAndType: ?DebugIDandTypename,
|
546
|
+
environment: IEnvironment,
|
547
|
+
fragmentNode: ReaderFragment,
|
548
|
+
componentDisplayName: string,
|
549
|
+
): void {
|
550
|
+
const {Record} = require('relay-runtime');
|
551
|
+
if (!previousIDAndType) {
|
552
|
+
return;
|
553
|
+
}
|
554
|
+
const recordSource = environment.getStore().getSource();
|
555
|
+
const record = recordSource.get(previousIDAndType.id);
|
556
|
+
const typename = record && Record.getType(record);
|
557
|
+
if (typename !== previousIDAndType.typename) {
|
558
|
+
warning(
|
559
|
+
false,
|
560
|
+
'Relay: Call to `refetch` returned data with a different ' +
|
561
|
+
'__typename: was `%s`, now `%s`, on `%s` in `%s`. ' +
|
562
|
+
'Please make sure the server correctly implements' +
|
563
|
+
'unique id requirement.',
|
564
|
+
previousIDAndType.typename,
|
565
|
+
typename,
|
566
|
+
fragmentNode.name,
|
567
|
+
componentDisplayName,
|
568
|
+
);
|
569
|
+
}
|
570
|
+
},
|
571
|
+
|
572
|
+
checkSameIDAfterRefetch(
|
573
|
+
previousIDAndTypename: ?DebugIDandTypename,
|
574
|
+
refetchedFragmentRef: mixed,
|
575
|
+
fragmentNode: ReaderFragment,
|
576
|
+
componentDisplayName: string,
|
577
|
+
): void {
|
578
|
+
if (previousIDAndTypename == null) {
|
579
|
+
return;
|
580
|
+
}
|
581
|
+
const {ID_KEY} = require('relay-runtime');
|
582
|
+
// $FlowExpectedError[incompatible-use]
|
583
|
+
const resultID = refetchedFragmentRef[ID_KEY];
|
584
|
+
if (resultID != null && resultID !== previousIDAndTypename.id) {
|
585
|
+
warning(
|
586
|
+
false,
|
587
|
+
'Relay: Call to `refetch` returned a different id, expected ' +
|
588
|
+
'`%s`, got `%s`, on `%s` in `%s`. ' +
|
589
|
+
'Please make sure the server correctly implements ' +
|
590
|
+
'unique id requirement.',
|
591
|
+
resultID,
|
592
|
+
previousIDAndTypename.id,
|
593
|
+
fragmentNode.name,
|
594
|
+
componentDisplayName,
|
595
|
+
);
|
596
|
+
}
|
597
|
+
},
|
598
|
+
};
|
599
|
+
}
|
600
|
+
|
601
|
+
module.exports = useRefetchableFragmentNode;
|
@@ -0,0 +1,65 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*
|
7
|
+
* @flow strict-local
|
8
|
+
* @format
|
9
|
+
* @oncall relay
|
10
|
+
*/
|
11
|
+
|
12
|
+
'use strict';
|
13
|
+
|
14
|
+
import type {RefetchFnDynamic} from './useRefetchableFragmentInternal_REACT_CACHE';
|
15
|
+
import type {
|
16
|
+
FragmentType,
|
17
|
+
GraphQLTaggedNode,
|
18
|
+
OperationType,
|
19
|
+
} from 'relay-runtime';
|
20
|
+
|
21
|
+
const useStaticFragmentNodeWarning = require('../useStaticFragmentNodeWarning');
|
22
|
+
const useRefetchableFragmentInternal = require('./useRefetchableFragmentInternal_REACT_CACHE');
|
23
|
+
const {useDebugValue} = require('react');
|
24
|
+
const {getFragment} = require('relay-runtime');
|
25
|
+
|
26
|
+
type ReturnType<TQuery: OperationType, TKey: ?{+$data?: mixed, ...}> = [
|
27
|
+
// NOTE: This $Call ensures that the type of the returned data is either:
|
28
|
+
// - nullable if the provided ref type is nullable
|
29
|
+
// - non-nullable if the provided ref type is non-nullable
|
30
|
+
// prettier-ignore
|
31
|
+
$Call<
|
32
|
+
& (<TFragmentData>( { +$data?: TFragmentData, ... }) => TFragmentData)
|
33
|
+
& (<TFragmentData>(?{ +$data?: TFragmentData, ... }) => ?TFragmentData),
|
34
|
+
TKey,
|
35
|
+
>,
|
36
|
+
RefetchFnDynamic<TQuery, TKey>,
|
37
|
+
];
|
38
|
+
|
39
|
+
function useRefetchableFragment<
|
40
|
+
TQuery: OperationType,
|
41
|
+
TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
|
42
|
+
>(
|
43
|
+
fragmentInput: GraphQLTaggedNode,
|
44
|
+
fragmentRef: TKey,
|
45
|
+
): ReturnType<TQuery, TKey> {
|
46
|
+
const fragmentNode = getFragment(fragmentInput);
|
47
|
+
useStaticFragmentNodeWarning(
|
48
|
+
fragmentNode,
|
49
|
+
'first argument of useRefetchableFragment()',
|
50
|
+
);
|
51
|
+
const {fragmentData, refetch} = useRefetchableFragmentInternal<TQuery, TKey>(
|
52
|
+
fragmentNode,
|
53
|
+
fragmentRef,
|
54
|
+
'useRefetchableFragment()',
|
55
|
+
);
|
56
|
+
if (__DEV__) {
|
57
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
58
|
+
useDebugValue({fragment: fragmentNode.name, data: fragmentData});
|
59
|
+
}
|
60
|
+
/* $FlowExpectedError[prop-missing] : Exposed options is a subset of internal
|
61
|
+
* options */
|
62
|
+
return [fragmentData, (refetch: RefetchFnDynamic<TQuery, TKey>)];
|
63
|
+
}
|
64
|
+
|
65
|
+
module.exports = useRefetchableFragment;
|