react-relay 16.1.0 → 17.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/ReactRelayContext.js +1 -1
- package/ReactRelayFragmentContainer.js.flow +8 -6
- package/ReactRelayLocalQueryRenderer.js.flow +4 -1
- package/ReactRelayPaginationContainer.js.flow +2 -0
- package/ReactRelayQueryRenderer.js.flow +1 -1
- package/ReactRelayTypes.js.flow +1 -0
- package/buildReactRelayContainer.js.flow +5 -3
- package/getRootVariablesForFragments.js.flow +1 -0
- package/hooks.js +1 -1
- package/hooks.js.flow +1 -1
- package/index.js +1 -1
- package/index.js.flow +1 -1
- package/legacy.js +1 -1
- package/lib/ReactRelayFragmentContainer.js +2 -2
- package/lib/buildReactRelayContainer.js +3 -3
- package/lib/relay-hooks/SuspenseResource.js +7 -4
- package/lib/relay-hooks/{FragmentResource.js → legacy/FragmentResource.js} +19 -20
- package/lib/relay-hooks/{useBlockingPaginationFragment.js → legacy/useBlockingPaginationFragment.js} +2 -2
- package/lib/relay-hooks/{useFragmentNode.js → legacy/useFragmentNode.js} +2 -2
- package/lib/relay-hooks/{useRefetchableFragmentNode.js → legacy/useRefetchableFragmentNode.js} +8 -8
- package/lib/relay-hooks/{experimental/readFragmentInternal_EXPERIMENTAL.js → readFragmentInternal.js} +7 -5
- package/lib/relay-hooks/useFragment.js +3 -13
- package/lib/relay-hooks/{experimental/useFragmentInternal_EXPERIMENTAL.js → useFragmentInternal.js} +67 -31
- package/lib/relay-hooks/useLazyLoadQueryNode.js +2 -13
- package/lib/relay-hooks/usePaginationFragment.js +17 -13
- package/lib/relay-hooks/useRefetchableFragment.js +3 -12
- package/lib/relay-hooks/{experimental/useRefetchableFragmentInternal_EXPERIMENTAL.js → useRefetchableFragmentInternal.js} +7 -7
- package/multi-actor/useRelayActorEnvironment.js.flow +1 -1
- 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/EntryPointTypes.flow.js.flow +22 -27
- package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +14 -1
- package/relay-hooks/NestedRelayEntryPointBuilderUtils.js.flow +5 -11
- package/relay-hooks/SuspenseResource.js.flow +11 -8
- package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +22 -1
- package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +19 -0
- package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +22 -0
- package/relay-hooks/{FragmentResource.js.flow → legacy/FragmentResource.js.flow} +21 -21
- package/relay-hooks/{useBlockingPaginationFragment.js.flow → legacy/useBlockingPaginationFragment.js.flow} +6 -6
- package/relay-hooks/{useFragmentNode.js.flow → legacy/useFragmentNode.js.flow} +3 -3
- package/relay-hooks/{useRefetchableFragmentNode.js.flow → legacy/useRefetchableFragmentNode.js.flow} +9 -9
- package/relay-hooks/loadQuery.js.flow +9 -8
- package/relay-hooks/{experimental/readFragmentInternal_EXPERIMENTAL.js.flow → readFragmentInternal.js.flow} +8 -4
- package/relay-hooks/useClientQuery.js.flow +1 -1
- package/relay-hooks/useEntryPointLoader.js.flow +1 -1
- package/relay-hooks/useFetchTrackingRef.js.flow +1 -1
- package/relay-hooks/useFragment.js.flow +16 -22
- package/relay-hooks/{experimental/useFragmentInternal_EXPERIMENTAL.js.flow → useFragmentInternal.js.flow} +71 -19
- package/relay-hooks/useIsMountedRef.js.flow +1 -1
- package/relay-hooks/useIsOperationNodeActive.js.flow +1 -1
- package/relay-hooks/useIsParentQueryActive.js.flow +5 -2
- package/relay-hooks/useLazyLoadQuery.js.flow +3 -2
- package/relay-hooks/useLazyLoadQueryNode.js.flow +3 -19
- package/relay-hooks/useLoadMoreFunction.js.flow +1 -1
- package/relay-hooks/useMemoOperationDescriptor.js.flow +1 -1
- package/relay-hooks/useMemoVariables.js.flow +1 -1
- package/relay-hooks/useMutation.js.flow +1 -1
- package/relay-hooks/usePaginationFragment.js.flow +62 -50
- package/relay-hooks/usePreloadedQuery.js.flow +2 -1
- package/relay-hooks/useQueryLoader.js.flow +2 -5
- package/relay-hooks/useRefetchableFragment.js.flow +7 -37
- package/relay-hooks/{experimental/useRefetchableFragmentInternal_EXPERIMENTAL.js.flow → useRefetchableFragmentInternal.js.flow} +11 -11
- package/relay-hooks/useRelayEnvironment.js.flow +1 -1
- package/relay-hooks/useStaticFragmentNodeWarning.js.flow +3 -1
- package/relay-hooks/useSubscribeToInvalidationState.js.flow +1 -1
- package/relay-hooks/useSubscription.js.flow +1 -1
- package/relay-hooks/useUnsafeRef_DEPRECATED.js.flow +1 -1
- package/lib/relay-hooks/HooksImplementation.js +0 -15
- package/lib/relay-hooks/experimental/useFragment_EXPERIMENTAL.js +0 -26
- package/lib/relay-hooks/experimental/usePaginationFragment_EXPERIMENTAL.js +0 -127
- package/lib/relay-hooks/experimental/useRefetchableFragment_EXPERIMENTAL.js +0 -23
- package/relay-hooks/HooksImplementation.js.flow +0 -45
- package/relay-hooks/experimental/useFragment_EXPERIMENTAL.js.flow +0 -66
- package/relay-hooks/experimental/usePaginationFragment_EXPERIMENTAL.js.flow +0 -161
- package/relay-hooks/experimental/useRefetchableFragment_EXPERIMENTAL.js.flow +0 -49
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
'use strict';
|
13
13
|
|
14
|
-
import type {QueryResult} from '
|
14
|
+
import type {QueryResult} from './QueryResource';
|
15
15
|
import type {
|
16
16
|
CacheConfig,
|
17
17
|
FetchPolicy,
|
@@ -26,12 +26,12 @@ import type {
|
|
26
26
|
MissingLiveResolverField,
|
27
27
|
} from 'relay-runtime/store/RelayStoreTypes';
|
28
28
|
|
29
|
-
const {getQueryResourceForEnvironment} = require('
|
30
|
-
const useRelayEnvironment = require('
|
29
|
+
const {getQueryResourceForEnvironment} = require('./QueryResource');
|
30
|
+
const useRelayEnvironment = require('./useRelayEnvironment');
|
31
31
|
const invariant = require('invariant');
|
32
32
|
const {useDebugValue, useEffect, useMemo, useRef, useState} = require('react');
|
33
33
|
const {
|
34
|
-
__internal: {fetchQuery: fetchQueryInternal},
|
34
|
+
__internal: {fetchQuery: fetchQueryInternal, getPromiseForActiveRequest},
|
35
35
|
RelayFeatureFlags,
|
36
36
|
areEqualSelectors,
|
37
37
|
createOperationDescriptor,
|
@@ -117,6 +117,8 @@ function handlePotentialSnapshotErrorsForState(
|
|
117
117
|
environment,
|
118
118
|
state.snapshot.missingRequiredFields,
|
119
119
|
state.snapshot.relayResolverErrors,
|
120
|
+
state.snapshot.errorResponseFields,
|
121
|
+
state.snapshot.selector.node.metadata?.throwOnFieldError ?? false,
|
120
122
|
);
|
121
123
|
} else if (state.kind === 'plural') {
|
122
124
|
for (const snapshot of state.snapshots) {
|
@@ -124,6 +126,8 @@ function handlePotentialSnapshotErrorsForState(
|
|
124
126
|
environment,
|
125
127
|
snapshot.missingRequiredFields,
|
126
128
|
snapshot.relayResolverErrors,
|
129
|
+
snapshot.errorResponseFields,
|
130
|
+
snapshot.selector.node.metadata?.throwOnFieldError ?? false,
|
127
131
|
);
|
128
132
|
}
|
129
133
|
}
|
@@ -162,6 +166,7 @@ function handleMissedUpdates(
|
|
162
166
|
selector: currentSnapshot.selector,
|
163
167
|
missingRequiredFields: currentSnapshot.missingRequiredFields,
|
164
168
|
relayResolverErrors: currentSnapshot.relayResolverErrors,
|
169
|
+
errorResponseFields: currentSnapshot.errorResponseFields,
|
165
170
|
};
|
166
171
|
return [
|
167
172
|
updatedData !== state.snapshot.data,
|
@@ -187,6 +192,7 @@ function handleMissedUpdates(
|
|
187
192
|
selector: currentSnapshot.selector,
|
188
193
|
missingRequiredFields: currentSnapshot.missingRequiredFields,
|
189
194
|
relayResolverErrors: currentSnapshot.relayResolverErrors,
|
195
|
+
errorResponseFields: currentSnapshot.errorResponseFields,
|
190
196
|
};
|
191
197
|
if (updatedData !== snapshot.data) {
|
192
198
|
didMissUpdates = true;
|
@@ -214,7 +220,7 @@ function handleMissingClientEdge(
|
|
214
220
|
parentFragmentRef: mixed,
|
215
221
|
missingClientEdgeRequestInfo: MissingClientEdgeRequestInfo,
|
216
222
|
queryOptions?: FragmentQueryOptions,
|
217
|
-
): QueryResult {
|
223
|
+
): [QueryResult, ?Promise<mixed>] {
|
218
224
|
const originalVariables = getVariablesFromFragment(
|
219
225
|
parentFragmentNode,
|
220
226
|
parentFragmentRef,
|
@@ -233,17 +239,23 @@ function handleMissingClientEdge(
|
|
233
239
|
// according to the component mount/suspense cycle; QueryResource
|
234
240
|
// already handles this by itself.
|
235
241
|
const QueryResource = getQueryResourceForEnvironment(environment);
|
236
|
-
|
242
|
+
const queryResult = QueryResource.prepare(
|
237
243
|
queryOperationDescriptor,
|
238
244
|
fetchQueryInternal(environment, queryOperationDescriptor),
|
239
245
|
queryOptions?.fetchPolicy,
|
240
246
|
);
|
247
|
+
|
248
|
+
return [
|
249
|
+
queryResult,
|
250
|
+
getPromiseForActiveRequest(environment, queryOperationDescriptor.request),
|
251
|
+
];
|
241
252
|
}
|
242
253
|
|
243
254
|
function subscribeToSnapshot(
|
244
255
|
environment: IEnvironment,
|
245
256
|
state: FragmentState,
|
246
257
|
setState: StateUpdaterFunction<FragmentState>,
|
258
|
+
hasPendingStateChanges: {current: boolean},
|
247
259
|
): () => void {
|
248
260
|
if (state.kind === 'bailout') {
|
249
261
|
return () => {};
|
@@ -264,11 +276,14 @@ function subscribeToSnapshot(
|
|
264
276
|
name: 'useFragment.subscription.missedUpdates',
|
265
277
|
hasDataChanges: dataChanged,
|
266
278
|
});
|
279
|
+
hasPendingStateChanges.current = dataChanged;
|
267
280
|
return dataChanged ? nextState : prevState;
|
268
281
|
} else {
|
269
282
|
return prevState;
|
270
283
|
}
|
271
284
|
}
|
285
|
+
|
286
|
+
hasPendingStateChanges.current = true;
|
272
287
|
return {
|
273
288
|
kind: 'singular',
|
274
289
|
snapshot: latestSnapshot,
|
@@ -297,6 +312,8 @@ function subscribeToSnapshot(
|
|
297
312
|
name: 'useFragment.subscription.missedUpdates',
|
298
313
|
hasDataChanges: dataChanged,
|
299
314
|
});
|
315
|
+
hasPendingStateChanges.current =
|
316
|
+
hasPendingStateChanges.current || dataChanged;
|
300
317
|
return dataChanged ? nextState : prevState;
|
301
318
|
} else {
|
302
319
|
return prevState;
|
@@ -304,6 +321,7 @@ function subscribeToSnapshot(
|
|
304
321
|
}
|
305
322
|
const updated = [...prevState.snapshots];
|
306
323
|
updated[index] = latestSnapshot;
|
324
|
+
hasPendingStateChanges.current = true;
|
307
325
|
return {
|
308
326
|
kind: 'plural',
|
309
327
|
snapshots: updated,
|
@@ -344,7 +362,7 @@ function getFragmentState(
|
|
344
362
|
}
|
345
363
|
|
346
364
|
// fragmentNode cannot change during the lifetime of the component, though fragmentRef may change.
|
347
|
-
|
365
|
+
hook useFragmentInternal(
|
348
366
|
fragmentNode: ReaderFragment,
|
349
367
|
fragmentRef: mixed,
|
350
368
|
hookDisplayName: string,
|
@@ -450,29 +468,38 @@ function useFragmentInternal_EXPERIMENTAL(
|
|
450
468
|
// a static (constant) property of the fragment. In practice, this effect will
|
451
469
|
// always or never run for a given invocation of this hook.
|
452
470
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
453
|
-
|
471
|
+
// $FlowFixMe[react-rule-hook]
|
472
|
+
const [clientEdgeQueries, activeRequestPromises] = useMemo(() => {
|
454
473
|
const missingClientEdges = getMissingClientEdges(state);
|
455
474
|
// eslint-disable-next-line no-shadow
|
456
475
|
let clientEdgeQueries;
|
476
|
+
const activeRequestPromises = [];
|
457
477
|
if (missingClientEdges?.length) {
|
458
478
|
clientEdgeQueries = ([]: Array<QueryResult>);
|
459
479
|
for (const edge of missingClientEdges) {
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
queryOptions,
|
467
|
-
),
|
480
|
+
const [queryResult, requestPromise] = handleMissingClientEdge(
|
481
|
+
environment,
|
482
|
+
fragmentNode,
|
483
|
+
fragmentRef,
|
484
|
+
edge,
|
485
|
+
queryOptions,
|
468
486
|
);
|
487
|
+
clientEdgeQueries.push(queryResult);
|
488
|
+
if (requestPromise != null) {
|
489
|
+
activeRequestPromises.push(requestPromise);
|
490
|
+
}
|
469
491
|
}
|
470
492
|
}
|
471
|
-
return clientEdgeQueries;
|
493
|
+
return [clientEdgeQueries, activeRequestPromises];
|
472
494
|
}, [state, environment, fragmentNode, fragmentRef, queryOptions]);
|
473
495
|
|
496
|
+
if (activeRequestPromises.length) {
|
497
|
+
throw Promise.all(activeRequestPromises);
|
498
|
+
}
|
499
|
+
|
474
500
|
// See above note
|
475
501
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
502
|
+
// $FlowFixMe[react-rule-hook]
|
476
503
|
useEffect(() => {
|
477
504
|
const QueryResource = getQueryResourceForEnvironment(environment);
|
478
505
|
if (clientEdgeQueries?.length) {
|
@@ -505,7 +532,10 @@ function useFragmentInternal_EXPERIMENTAL(
|
|
505
532
|
// We only suspend when the component is first trying to mount or changing
|
506
533
|
// selectors, not if data becomes missing later:
|
507
534
|
if (
|
535
|
+
RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE ||
|
536
|
+
environment !== previousEnvironment ||
|
508
537
|
!committedFragmentSelectorRef.current ||
|
538
|
+
// $FlowFixMe[react-rule-unsafe-ref]
|
509
539
|
!areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector)
|
510
540
|
) {
|
511
541
|
invariant(fragmentSelector != null, 'refinement, see invariants above');
|
@@ -528,6 +558,8 @@ function useFragmentInternal_EXPERIMENTAL(
|
|
528
558
|
// they're missing even though we are out of options for possibly fetching them:
|
529
559
|
handlePotentialSnapshotErrorsForState(environment, state);
|
530
560
|
|
561
|
+
const hasPendingStateChanges = useRef<boolean>(false);
|
562
|
+
|
531
563
|
useEffect(() => {
|
532
564
|
// Check for updates since the state was rendered
|
533
565
|
let currentState = subscribedState;
|
@@ -546,9 +578,27 @@ function useFragmentInternal_EXPERIMENTAL(
|
|
546
578
|
}
|
547
579
|
currentState = updatedState;
|
548
580
|
}
|
549
|
-
return subscribeToSnapshot(
|
581
|
+
return subscribeToSnapshot(
|
582
|
+
environment,
|
583
|
+
currentState,
|
584
|
+
setState,
|
585
|
+
hasPendingStateChanges,
|
586
|
+
);
|
550
587
|
}, [environment, subscribedState]);
|
551
588
|
|
589
|
+
if (hasPendingStateChanges.current) {
|
590
|
+
const updates = handleMissedUpdates(environment, state);
|
591
|
+
if (updates != null) {
|
592
|
+
const [hasStateUpdates, updatedState] = updates;
|
593
|
+
if (hasStateUpdates) {
|
594
|
+
setState(updatedState);
|
595
|
+
state = updatedState;
|
596
|
+
}
|
597
|
+
}
|
598
|
+
// $FlowFixMe[react-rule-unsafe-ref]
|
599
|
+
hasPendingStateChanges.current = false;
|
600
|
+
}
|
601
|
+
|
552
602
|
let data: ?SelectorData | Array<?SelectorData>;
|
553
603
|
if (isPlural) {
|
554
604
|
// Plural fragments require allocating an array of the snapshot data values,
|
@@ -558,6 +608,7 @@ function useFragmentInternal_EXPERIMENTAL(
|
|
558
608
|
// for a particular useFragment invocation site
|
559
609
|
const fragmentRefIsNullish = fragmentRef == null; // for less sensitive memoization
|
560
610
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
611
|
+
// $FlowFixMe[react-rule-hook]
|
561
612
|
data = useMemo(() => {
|
562
613
|
if (state.kind === 'bailout') {
|
563
614
|
// Bailout state can happen if the fragmentRef is a plural array that is empty or has no
|
@@ -608,10 +659,11 @@ function useFragmentInternal_EXPERIMENTAL(
|
|
608
659
|
|
609
660
|
if (__DEV__) {
|
610
661
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
662
|
+
// $FlowFixMe[react-rule-hook]
|
611
663
|
useDebugValue({fragment: fragmentNode.name, data});
|
612
664
|
}
|
613
665
|
|
614
666
|
return data;
|
615
667
|
}
|
616
668
|
|
617
|
-
module.exports =
|
669
|
+
module.exports = useFragmentInternal;
|
@@ -17,9 +17,12 @@ const useIsOperationNodeActive = require('./useIsOperationNodeActive');
|
|
17
17
|
const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
|
18
18
|
const {getFragment} = require('relay-runtime');
|
19
19
|
|
20
|
-
|
20
|
+
hook useIsParentQueryActive<
|
21
21
|
TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
|
22
|
-
>(
|
22
|
+
>(
|
23
|
+
fragmentInput: GraphQLTaggedNode,
|
24
|
+
fragmentRef: TKey,
|
25
|
+
): boolean {
|
23
26
|
const fragmentNode = getFragment(fragmentInput);
|
24
27
|
useStaticFragmentNodeWarning(
|
25
28
|
fragmentNode,
|
@@ -30,7 +30,7 @@ const {
|
|
30
30
|
// This separate type export is only needed as long as we are injecting
|
31
31
|
// a separate hooks implementation in ./HooksImplementation -- it can
|
32
32
|
// be removed after we stop doing that.
|
33
|
-
export type UseLazyLoadQueryHookType = <TVariables: Variables, TData>(
|
33
|
+
export type UseLazyLoadQueryHookType = hook <TVariables: Variables, TData>(
|
34
34
|
gqlQuery: Query<TVariables, TData>,
|
35
35
|
variables: TVariables,
|
36
36
|
options?: {
|
@@ -41,7 +41,7 @@ export type UseLazyLoadQueryHookType = <TVariables: Variables, TData>(
|
|
41
41
|
},
|
42
42
|
) => TData;
|
43
43
|
|
44
|
-
|
44
|
+
hook useLazyLoadQuery<TVariables: Variables, TData>(
|
45
45
|
gqlQuery: Query<TVariables, TData>,
|
46
46
|
variables: TVariables,
|
47
47
|
options?: {
|
@@ -75,4 +75,5 @@ function useLazyLoadQuery<TVariables: Variables, TData>(
|
|
75
75
|
return data;
|
76
76
|
}
|
77
77
|
|
78
|
+
// $FlowFixMe[react-rule-hook-incompatible]
|
78
79
|
module.exports = (useLazyLoadQuery: UseLazyLoadQueryHookType);
|
@@ -19,22 +19,20 @@ import type {
|
|
19
19
|
OperationType,
|
20
20
|
RenderPolicy,
|
21
21
|
} from 'relay-runtime';
|
22
|
-
import type {ReaderFragment} from 'relay-runtime/util/ReaderNode';
|
23
22
|
|
24
|
-
const HooksImplementation = require('./HooksImplementation');
|
25
23
|
const ProfilerContext = require('./ProfilerContext');
|
26
24
|
const {
|
27
25
|
getQueryCacheIdentifier,
|
28
26
|
getQueryResourceForEnvironment,
|
29
27
|
} = require('./QueryResource');
|
30
28
|
const useFetchTrackingRef = require('./useFetchTrackingRef');
|
31
|
-
const
|
29
|
+
const useFragmentInternal = require('./useFragmentInternal');
|
32
30
|
const useRelayEnvironment = require('./useRelayEnvironment');
|
33
31
|
const React = require('react');
|
34
32
|
|
35
33
|
const {useContext, useEffect, useState, useRef} = React;
|
36
34
|
|
37
|
-
|
35
|
+
hook useLazyLoadQueryNode<TQuery: OperationType>({
|
38
36
|
query,
|
39
37
|
componentDisplayName,
|
40
38
|
fetchObservable,
|
@@ -127,7 +125,7 @@ function useLazyLoadQueryNode<TQuery: OperationType>({
|
|
127
125
|
});
|
128
126
|
|
129
127
|
const {fragmentNode, fragmentRef} = preparedQueryResult;
|
130
|
-
const data =
|
128
|
+
const data = useFragmentInternal(
|
131
129
|
fragmentNode,
|
132
130
|
fragmentRef,
|
133
131
|
componentDisplayName,
|
@@ -135,18 +133,4 @@ function useLazyLoadQueryNode<TQuery: OperationType>({
|
|
135
133
|
return data;
|
136
134
|
}
|
137
135
|
|
138
|
-
function useFragmentNodeImpl(
|
139
|
-
fragment: ReaderFragment,
|
140
|
-
key: mixed,
|
141
|
-
componentDisplayName: string,
|
142
|
-
): mixed {
|
143
|
-
const impl = HooksImplementation.get();
|
144
|
-
if (impl && impl.useFragment__internal) {
|
145
|
-
return impl.useFragment__internal(fragment, key, componentDisplayName);
|
146
|
-
} else {
|
147
|
-
const {data} = useFragmentNode<mixed>(fragment, key, componentDisplayName);
|
148
|
-
return data;
|
149
|
-
}
|
150
|
-
}
|
151
|
-
|
152
136
|
module.exports = useLazyLoadQueryNode;
|
@@ -61,7 +61,7 @@ export type UseLoadMoreFunctionArgs = {
|
|
61
61
|
onReset: () => void,
|
62
62
|
};
|
63
63
|
|
64
|
-
|
64
|
+
hook useLoadMoreFunction<TVariables: Variables>(
|
65
65
|
args: UseLoadMoreFunctionArgs,
|
66
66
|
): [LoadMoreFn<TVariables>, boolean, () => void] {
|
67
67
|
const {
|
@@ -24,7 +24,7 @@ const {createOperationDescriptor, getRequest} = require('relay-runtime');
|
|
24
24
|
|
25
25
|
const {useMemo} = React;
|
26
26
|
|
27
|
-
|
27
|
+
hook useMemoOperationDescriptor(
|
28
28
|
gqlQuery: GraphQLTaggedNode,
|
29
29
|
variables: Variables,
|
30
30
|
cacheConfig?: ?CacheConfig,
|
@@ -21,7 +21,7 @@ const {useState} = require('react');
|
|
21
21
|
* This is useful when a `variables` object is used as a value in a depencency
|
22
22
|
* array as it might often be constructed during render.
|
23
23
|
*/
|
24
|
-
|
24
|
+
hook useMemoVariables<TVariables: Variables | null>(
|
25
25
|
variables: TVariables,
|
26
26
|
): TVariables {
|
27
27
|
const [mirroredVariables, setMirroredVariables] = useState(variables);
|
@@ -64,7 +64,7 @@ type UseMutationConfigInternal<TVariables, TData, TRawResponse> = {
|
|
64
64
|
variables: TVariables,
|
65
65
|
};
|
66
66
|
|
67
|
-
|
67
|
+
hook useMutation<TVariables: Variables, TData, TRawResponse = {...}>(
|
68
68
|
mutation: Mutation<TVariables, TData, TRawResponse>,
|
69
69
|
commitMutationFn?: (
|
70
70
|
environment: IEnvironment,
|
@@ -12,9 +12,9 @@
|
|
12
12
|
'use strict';
|
13
13
|
|
14
14
|
import type {LoadMoreFn, UseLoadMoreFunctionArgs} from './useLoadMoreFunction';
|
15
|
-
import type {
|
16
|
-
import type {Options} from './useRefetchableFragmentNode';
|
15
|
+
import type {Options} from './useRefetchableFragmentInternal';
|
17
16
|
import type {
|
17
|
+
Disposable,
|
18
18
|
FragmentType,
|
19
19
|
GraphQLResponse,
|
20
20
|
Observer,
|
@@ -22,9 +22,9 @@ import type {
|
|
22
22
|
Variables,
|
23
23
|
} from 'relay-runtime';
|
24
24
|
|
25
|
-
const HooksImplementation = require('./HooksImplementation');
|
26
25
|
const useLoadMoreFunction = require('./useLoadMoreFunction');
|
27
|
-
const
|
26
|
+
const useRefetchableFragmentInternal = require('./useRefetchableFragmentInternal');
|
27
|
+
const useRelayEnvironment = require('./useRelayEnvironment');
|
28
28
|
const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
|
29
29
|
const {useCallback, useDebugValue, useState} = require('react');
|
30
30
|
const {
|
@@ -33,6 +33,40 @@ const {
|
|
33
33
|
getPaginationMetadata,
|
34
34
|
} = require('relay-runtime');
|
35
35
|
|
36
|
+
type RefetchVariables<TVariables, TKey: ?{+$fragmentSpreads: mixed, ...}> =
|
37
|
+
// NOTE: This type ensures that the type of the returned variables is either:
|
38
|
+
// - nullable if the provided ref type is nullable
|
39
|
+
// - non-nullable if the provided ref type is non-nullable
|
40
|
+
[+key: TKey] extends [+key: {+$fragmentSpreads: mixed, ...}]
|
41
|
+
? Partial<TVariables>
|
42
|
+
: TVariables;
|
43
|
+
|
44
|
+
type RefetchFnBase<TVars, TOptions> = (
|
45
|
+
vars: TVars,
|
46
|
+
options?: TOptions,
|
47
|
+
) => Disposable;
|
48
|
+
|
49
|
+
type RefetchFn<TVariables, TKey, TOptions = Options> = RefetchFnBase<
|
50
|
+
RefetchVariables<TVariables, TKey>,
|
51
|
+
TOptions,
|
52
|
+
>;
|
53
|
+
|
54
|
+
export type ReturnType<TVariables, TData, TKey> = {
|
55
|
+
// NOTE: This type ensures that the type of the returned data is either:
|
56
|
+
// - nullable if the provided ref type is nullable
|
57
|
+
// - non-nullable if the provided ref type is non-nullable
|
58
|
+
data: [+key: TKey] extends [+key: {+$fragmentSpreads: mixed, ...}]
|
59
|
+
? TData
|
60
|
+
: ?TData,
|
61
|
+
loadNext: LoadMoreFn<TVariables>,
|
62
|
+
loadPrevious: LoadMoreFn<TVariables>,
|
63
|
+
hasNext: boolean,
|
64
|
+
hasPrevious: boolean,
|
65
|
+
isLoadingNext: boolean,
|
66
|
+
isLoadingPrevious: boolean,
|
67
|
+
refetch: RefetchFn<TVariables, TKey>,
|
68
|
+
};
|
69
|
+
|
36
70
|
// This separate type export is only needed as long as we are injecting
|
37
71
|
// a separate hooks implementation in ./HooksImplementation -- it can
|
38
72
|
// be removed after we stop doing that.
|
@@ -46,7 +80,7 @@ export type UsePaginationFragmentType = <
|
|
46
80
|
parentFragmentRef: TKey,
|
47
81
|
) => ReturnType<TVariables, TData, TKey>;
|
48
82
|
|
49
|
-
|
83
|
+
hook usePaginationFragment<
|
50
84
|
TFragmentType: FragmentType,
|
51
85
|
TVariables: Variables,
|
52
86
|
TData,
|
@@ -65,9 +99,9 @@ function usePaginationFragment_LEGACY<
|
|
65
99
|
const {connectionPathInFragmentData, paginationRequest, paginationMetadata} =
|
66
100
|
getPaginationMetadata(fragmentNode, componentDisplayName);
|
67
101
|
|
68
|
-
const {fragmentData, fragmentRef, refetch} =
|
69
|
-
|
70
|
-
|
102
|
+
const {fragmentData, fragmentRef, refetch} = useRefetchableFragmentInternal<
|
103
|
+
{variables: TVariables, response: TData},
|
104
|
+
{data?: TData},
|
71
105
|
>(fragmentNode, parentFragmentRef, componentDisplayName);
|
72
106
|
const fragmentIdentifier = getFragmentIdentifier(fragmentNode, fragmentRef);
|
73
107
|
|
@@ -99,7 +133,7 @@ function usePaginationFragment_LEGACY<
|
|
99
133
|
paginationRequest,
|
100
134
|
});
|
101
135
|
|
102
|
-
const refetchPagination
|
136
|
+
const refetchPagination = useCallback(
|
103
137
|
(variables: TVariables, options: void | Options) => {
|
104
138
|
disposeFetchNext();
|
105
139
|
disposeFetchPrevious();
|
@@ -110,6 +144,7 @@ function usePaginationFragment_LEGACY<
|
|
110
144
|
|
111
145
|
if (__DEV__) {
|
112
146
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
147
|
+
// $FlowFixMe[react-rule-hook]
|
113
148
|
useDebugValue({
|
114
149
|
fragment: fragmentNode.name,
|
115
150
|
data: fragmentData,
|
@@ -120,7 +155,8 @@ function usePaginationFragment_LEGACY<
|
|
120
155
|
});
|
121
156
|
}
|
122
157
|
return {
|
123
|
-
|
158
|
+
// $FlowFixMe[incompatible-return]
|
159
|
+
data: fragmentData,
|
124
160
|
loadNext,
|
125
161
|
loadPrevious,
|
126
162
|
hasNext,
|
@@ -131,7 +167,7 @@ function usePaginationFragment_LEGACY<
|
|
131
167
|
};
|
132
168
|
}
|
133
169
|
|
134
|
-
|
170
|
+
hook useLoadMore<TVariables: Variables>(
|
135
171
|
args: $Diff<
|
136
172
|
UseLoadMoreFunctionArgs,
|
137
173
|
{
|
@@ -141,7 +177,21 @@ function useLoadMore<TVariables: Variables>(
|
|
141
177
|
},
|
142
178
|
>,
|
143
179
|
): [LoadMoreFn<TVariables>, boolean, boolean, () => void] {
|
144
|
-
const
|
180
|
+
const environment = useRelayEnvironment();
|
181
|
+
const [isLoadingMore, reallySetIsLoadingMore] = useState(false);
|
182
|
+
// Schedule this update since it must be observed by components at the same
|
183
|
+
// batch as when hasNext changes. hasNext is read from the store and store
|
184
|
+
// updates are scheduled, so this must be scheduled too.
|
185
|
+
const setIsLoadingMore = (value: boolean) => {
|
186
|
+
const schedule = environment.getScheduler()?.schedule;
|
187
|
+
if (schedule) {
|
188
|
+
schedule(() => {
|
189
|
+
reallySetIsLoadingMore(value);
|
190
|
+
});
|
191
|
+
} else {
|
192
|
+
reallySetIsLoadingMore(value);
|
193
|
+
}
|
194
|
+
};
|
145
195
|
const observer = {
|
146
196
|
start: () => setIsLoadingMore(true),
|
147
197
|
complete: () => setIsLoadingMore(false),
|
@@ -156,42 +206,4 @@ function useLoadMore<TVariables: Variables>(
|
|
156
206
|
return [loadMore, hasMore, isLoadingMore, disposeFetch];
|
157
207
|
}
|
158
208
|
|
159
|
-
export type ReturnType<TVariables, TData, TKey> = {
|
160
|
-
// NOTE: This type ensures that the type of the returned data is either:
|
161
|
-
// - nullable if the provided ref type is nullable
|
162
|
-
// - non-nullable if the provided ref type is non-nullable
|
163
|
-
data: [+key: TKey] extends [+key: {+$fragmentSpreads: mixed, ...}]
|
164
|
-
? TData
|
165
|
-
: ?TData,
|
166
|
-
loadNext: LoadMoreFn<TVariables>,
|
167
|
-
loadPrevious: LoadMoreFn<TVariables>,
|
168
|
-
hasNext: boolean,
|
169
|
-
hasPrevious: boolean,
|
170
|
-
isLoadingNext: boolean,
|
171
|
-
isLoadingPrevious: boolean,
|
172
|
-
refetch: RefetchFn<TVariables, TKey>,
|
173
|
-
};
|
174
|
-
|
175
|
-
function usePaginationFragment<
|
176
|
-
TFragmentType: FragmentType,
|
177
|
-
TVariables: Variables,
|
178
|
-
TData,
|
179
|
-
TKey: ?{+$fragmentSpreads: TFragmentType, ...},
|
180
|
-
>(
|
181
|
-
fragmentInput: RefetchableFragment<TFragmentType, TData, TVariables>,
|
182
|
-
parentFragmentRef: TKey,
|
183
|
-
): ReturnType<TVariables, TData, TKey> {
|
184
|
-
const impl = HooksImplementation.get();
|
185
|
-
if (impl) {
|
186
|
-
// $FlowExpectedError[incompatible-return] Flow cannot prove that two conditional type satisfy each other
|
187
|
-
return impl.usePaginationFragment<TFragmentType, TVariables, TData, TKey>(
|
188
|
-
fragmentInput,
|
189
|
-
parentFragmentRef,
|
190
|
-
);
|
191
|
-
} else {
|
192
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
193
|
-
return usePaginationFragment_LEGACY(fragmentInput, parentFragmentRef);
|
194
|
-
}
|
195
|
-
}
|
196
|
-
|
197
209
|
module.exports = usePaginationFragment;
|
@@ -52,7 +52,7 @@ type PreloadedQuery<
|
|
52
52
|
TEnvironmentProviderOptions,
|
53
53
|
>;
|
54
54
|
|
55
|
-
|
55
|
+
hook usePreloadedQuery<
|
56
56
|
TVariables: Variables,
|
57
57
|
TData,
|
58
58
|
TRawResponse: ?{...} = void,
|
@@ -164,6 +164,7 @@ function usePreloadedQuery<
|
|
164
164
|
|
165
165
|
if (__DEV__) {
|
166
166
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
167
|
+
// $FlowFixMe[react-rule-hook]
|
167
168
|
useDebugValue({
|
168
169
|
query: preloadedQuery.name,
|
169
170
|
variables: preloadedQuery.variables,
|
@@ -108,11 +108,7 @@ declare function useQueryLoader<TQuery: OperationType>(
|
|
108
108
|
initialQueryReference?: ?PreloadedQuery<TQuery>,
|
109
109
|
): UseQueryLoaderHookReturnType<TQuery['variables'], TQuery['response']>;
|
110
110
|
|
111
|
-
|
112
|
-
TVariables: Variables,
|
113
|
-
TData,
|
114
|
-
TRawResponse: ?{...} = void,
|
115
|
-
>(
|
111
|
+
hook useQueryLoader<TVariables: Variables, TData, TRawResponse: ?{...} = void>(
|
116
112
|
preloadableRequest: Query<TVariables, TData, TRawResponse>,
|
117
113
|
initialQueryReference?: ?PreloadedQuery<{
|
118
114
|
response: TData,
|
@@ -172,6 +168,7 @@ function useQueryLoader<
|
|
172
168
|
// necessary here
|
173
169
|
// TODO(T78446637): Handle disposal of managed query references in
|
174
170
|
// components that were never mounted after rendering
|
171
|
+
// $FlowFixMe[react-rule-unsafe-ref]
|
175
172
|
undisposedQueryReferencesRef.current.add(initialQueryReferenceInternal);
|
176
173
|
setPreviousInitialQueryReference(initialQueryReferenceInternal);
|
177
174
|
setQueryReference(initialQueryReferenceInternal);
|