react-relay 16.1.0 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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);
|