react-relay 18.0.0 → 18.1.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/buildReactRelayContainer.js.flow +1 -0
- package/hooks.js +1 -1
- package/index.js +1 -1
- package/legacy.js +1 -1
- package/lib/relay-hooks/getConnectionState.js +47 -0
- package/lib/relay-hooks/legacy/FragmentResource.js +2 -6
- package/lib/relay-hooks/readFragmentInternal.js +2 -4
- package/lib/relay-hooks/useFragmentInternal.js +1 -1
- package/lib/relay-hooks/useFragmentInternal_CURRENT.js +2 -8
- package/lib/relay-hooks/useFragmentInternal_EXPERIMENTAL.js +5 -26
- package/lib/relay-hooks/useLoadMoreFunction.js +10 -43
- package/lib/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js +130 -0
- package/lib/relay-hooks/useQueryLoader.js +8 -0
- package/lib/relay-hooks/useQueryLoader_EXPERIMENTAL.js +120 -0
- 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 +1 -1
- package/react-relay-legacy.min.js +1 -1
- package/react-relay.js +2 -2
- package/react-relay.min.js +2 -2
- package/relay-hooks/getConnectionState.js.flow +97 -0
- package/relay-hooks/legacy/FragmentResource.js.flow +2 -13
- package/relay-hooks/readFragmentInternal.js.flow +1 -10
- package/relay-hooks/useFragmentInternal.js.flow +1 -1
- package/relay-hooks/useFragmentInternal_CURRENT.js.flow +2 -15
- package/relay-hooks/useFragmentInternal_EXPERIMENTAL.js.flow +3 -49
- package/relay-hooks/useLoadMoreFunction.js.flow +14 -80
- package/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js.flow +280 -0
- package/relay-hooks/useQueryLoader.js.flow +27 -3
- package/relay-hooks/useQueryLoader_EXPERIMENTAL.js.flow +253 -0
@@ -127,20 +127,11 @@ function handlePotentialSnapshotErrorsForState(
|
|
127
127
|
if (state.kind === 'singular') {
|
128
128
|
handlePotentialSnapshotErrors(
|
129
129
|
environment,
|
130
|
-
state.snapshot.missingRequiredFields,
|
131
|
-
state.snapshot.relayResolverErrors,
|
132
130
|
state.snapshot.errorResponseFields,
|
133
|
-
state.snapshot.selector.node.metadata?.throwOnFieldError ?? false,
|
134
131
|
);
|
135
132
|
} else if (state.kind === 'plural') {
|
136
133
|
for (const snapshot of state.snapshots) {
|
137
|
-
handlePotentialSnapshotErrors(
|
138
|
-
environment,
|
139
|
-
snapshot.missingRequiredFields,
|
140
|
-
snapshot.relayResolverErrors,
|
141
|
-
snapshot.errorResponseFields,
|
142
|
-
snapshot.selector.node.metadata?.throwOnFieldError ?? false,
|
143
|
-
);
|
134
|
+
handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
|
144
135
|
}
|
145
136
|
}
|
146
137
|
}
|
@@ -176,8 +167,6 @@ function handleMissedUpdates(
|
|
176
167
|
missingLiveResolverFields: currentSnapshot.missingLiveResolverFields,
|
177
168
|
seenRecords: currentSnapshot.seenRecords,
|
178
169
|
selector: currentSnapshot.selector,
|
179
|
-
missingRequiredFields: currentSnapshot.missingRequiredFields,
|
180
|
-
relayResolverErrors: currentSnapshot.relayResolverErrors,
|
181
170
|
errorResponseFields: currentSnapshot.errorResponseFields,
|
182
171
|
};
|
183
172
|
return [
|
@@ -204,8 +193,6 @@ function handleMissedUpdates(
|
|
204
193
|
missingLiveResolverFields: currentSnapshot.missingLiveResolverFields,
|
205
194
|
seenRecords: currentSnapshot.seenRecords,
|
206
195
|
selector: currentSnapshot.selector,
|
207
|
-
missingRequiredFields: currentSnapshot.missingRequiredFields,
|
208
|
-
relayResolverErrors: currentSnapshot.relayResolverErrors,
|
209
196
|
errorResponseFields: currentSnapshot.errorResponseFields,
|
210
197
|
};
|
211
198
|
if (updatedData !== snapshot.data) {
|
@@ -271,7 +258,6 @@ function subscribeToSnapshot(
|
|
271
258
|
environment: IEnvironment,
|
272
259
|
state: FragmentState,
|
273
260
|
setState: StateUpdaterFunction<FragmentState>,
|
274
|
-
pendingStateRef: {current: number | null},
|
275
261
|
): () => void {
|
276
262
|
if (state.kind === 'bailout') {
|
277
263
|
return () => {};
|
@@ -307,8 +293,6 @@ function subscribeToSnapshot(
|
|
307
293
|
environment: state.environment,
|
308
294
|
};
|
309
295
|
}
|
310
|
-
pendingStateRef.current =
|
311
|
-
nextState.kind === 'singular' ? nextState.epoch : null;
|
312
296
|
return nextState;
|
313
297
|
});
|
314
298
|
});
|
@@ -353,8 +337,6 @@ function subscribeToSnapshot(
|
|
353
337
|
environment: state.environment,
|
354
338
|
};
|
355
339
|
}
|
356
|
-
pendingStateRef.current =
|
357
|
-
nextState.kind === 'plural' ? nextState.epoch : null;
|
358
340
|
return nextState;
|
359
341
|
});
|
360
342
|
}),
|
@@ -542,7 +524,7 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
542
524
|
if (suspendingLiveResolvers != null && suspendingLiveResolvers.length > 0) {
|
543
525
|
throw Promise.all(
|
544
526
|
suspendingLiveResolvers.map(({liveStateID}) => {
|
545
|
-
// $FlowFixMe[prop-missing] This is expected to be a
|
527
|
+
// $FlowFixMe[prop-missing] This is expected to be a RelayModernStore
|
546
528
|
return environment.getStore().getLiveResolverPromise(liveStateID);
|
547
529
|
}),
|
548
530
|
);
|
@@ -578,11 +560,6 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
578
560
|
// they're missing even though we are out of options for possibly fetching them:
|
579
561
|
handlePotentialSnapshotErrorsForState(environment, state);
|
580
562
|
|
581
|
-
// Ref that stores the epoch of the pending setState, if any. This is used to check
|
582
|
-
// if the state we're rendering is at least as current as the pending update, and
|
583
|
-
// force a refresh if stale.
|
584
|
-
const pendingStateEpochRef = useRef<number | null>(null);
|
585
|
-
|
586
563
|
// We emulate CRUD effects using a ref and two effects:
|
587
564
|
// - The ref tracks the current state (including updates from the subscription)
|
588
565
|
// and the dispose function for the current subscription. This is null until
|
@@ -647,7 +624,6 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
647
624
|
state.environment,
|
648
625
|
stateForSubscription,
|
649
626
|
setState,
|
650
|
-
pendingStateEpochRef,
|
651
627
|
);
|
652
628
|
storeSubscriptionRef.current = {
|
653
629
|
dispose,
|
@@ -657,12 +633,7 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
657
633
|
}, [state]);
|
658
634
|
useEffect(() => {
|
659
635
|
if (storeSubscriptionRef.current == null && state.kind !== 'bailout') {
|
660
|
-
const dispose = subscribeToSnapshot(
|
661
|
-
state.environment,
|
662
|
-
state,
|
663
|
-
setState,
|
664
|
-
pendingStateEpochRef,
|
665
|
-
);
|
636
|
+
const dispose = subscribeToSnapshot(state.environment, state, setState);
|
666
637
|
storeSubscriptionRef.current = {
|
667
638
|
dispose,
|
668
639
|
selector: state.selector,
|
@@ -677,23 +648,6 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
677
648
|
// simulating a CRUD effect
|
678
649
|
}, []);
|
679
650
|
|
680
|
-
// If a low-priority update was queued and hasn't rendered yet, render it now
|
681
|
-
if (
|
682
|
-
pendingStateEpochRef.current !== null &&
|
683
|
-
pendingStateEpochRef.current !== state.epoch
|
684
|
-
) {
|
685
|
-
const updates = handleMissedUpdates(environment, state);
|
686
|
-
if (updates != null) {
|
687
|
-
const [hasStateUpdates, updatedState] = updates;
|
688
|
-
if (hasStateUpdates) {
|
689
|
-
setState(updatedState);
|
690
|
-
state = updatedState;
|
691
|
-
}
|
692
|
-
}
|
693
|
-
}
|
694
|
-
// $FlowFixMe[react-rule-unsafe-ref]
|
695
|
-
pendingStateEpochRef.current = null;
|
696
|
-
|
697
651
|
let data: ?SelectorData | Array<?SelectorData>;
|
698
652
|
if (isPlural) {
|
699
653
|
// Plural fragments require allocating an array of the snapshot data values,
|
@@ -22,20 +22,21 @@ import type {
|
|
22
22
|
Variables,
|
23
23
|
} from 'relay-runtime';
|
24
24
|
|
25
|
+
const getConnectionState = require('./getConnectionState');
|
25
26
|
const useFetchTrackingRef = require('./useFetchTrackingRef');
|
26
27
|
const useIsMountedRef = require('./useIsMountedRef');
|
27
28
|
const useIsOperationNodeActive = require('./useIsOperationNodeActive');
|
29
|
+
const useLoadMoreFunction_EXPERIMENTAL = require('./useLoadMoreFunction_EXPERIMENTAL');
|
28
30
|
const useRelayEnvironment = require('./useRelayEnvironment');
|
29
31
|
const invariant = require('invariant');
|
30
32
|
const {useCallback, useEffect, useState} = require('react');
|
31
33
|
const {
|
32
34
|
__internal: {fetchQuery},
|
33
|
-
|
35
|
+
RelayFeatureFlags,
|
34
36
|
createOperationDescriptor,
|
35
37
|
getPaginationVariables,
|
36
38
|
getRefetchMetadata,
|
37
39
|
getSelector,
|
38
|
-
getValueAtPath,
|
39
40
|
} = require('relay-runtime');
|
40
41
|
const warning = require('warning');
|
41
42
|
|
@@ -63,6 +64,17 @@ export type UseLoadMoreFunctionArgs = {
|
|
63
64
|
|
64
65
|
hook useLoadMoreFunction<TVariables: Variables>(
|
65
66
|
args: UseLoadMoreFunctionArgs,
|
67
|
+
): [LoadMoreFn<TVariables>, boolean, () => void] {
|
68
|
+
if (RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY) {
|
69
|
+
// $FlowFixMe[react-rule-hook] - the condition is static
|
70
|
+
return useLoadMoreFunction_EXPERIMENTAL(args);
|
71
|
+
}
|
72
|
+
// $FlowFixMe[react-rule-hook] - the condition is static
|
73
|
+
return useLoadMoreFunction_CURRENT(args);
|
74
|
+
}
|
75
|
+
|
76
|
+
hook useLoadMoreFunction_CURRENT<TVariables: Variables>(
|
77
|
+
args: UseLoadMoreFunctionArgs,
|
66
78
|
): [LoadMoreFn<TVariables>, boolean, () => void] {
|
67
79
|
const {
|
68
80
|
direction,
|
@@ -269,82 +281,4 @@ hook useLoadMoreFunction<TVariables: Variables>(
|
|
269
281
|
return [loadMore, hasMore, disposeFetch];
|
270
282
|
}
|
271
283
|
|
272
|
-
function getConnectionState(
|
273
|
-
direction: Direction,
|
274
|
-
fragmentNode: ReaderFragment,
|
275
|
-
fragmentData: mixed,
|
276
|
-
connectionPathInFragmentData: $ReadOnlyArray<string | number>,
|
277
|
-
): {
|
278
|
-
cursor: ?string,
|
279
|
-
hasMore: boolean,
|
280
|
-
} {
|
281
|
-
const {
|
282
|
-
EDGES,
|
283
|
-
PAGE_INFO,
|
284
|
-
HAS_NEXT_PAGE,
|
285
|
-
HAS_PREV_PAGE,
|
286
|
-
END_CURSOR,
|
287
|
-
START_CURSOR,
|
288
|
-
} = ConnectionInterface.get();
|
289
|
-
const connection = getValueAtPath(fragmentData, connectionPathInFragmentData);
|
290
|
-
if (connection == null) {
|
291
|
-
return {cursor: null, hasMore: false};
|
292
|
-
}
|
293
|
-
|
294
|
-
invariant(
|
295
|
-
typeof connection === 'object',
|
296
|
-
'Relay: Expected connection in fragment `%s` to have been `null`, or ' +
|
297
|
-
'a plain object with %s and %s properties. Instead got `%s`.',
|
298
|
-
fragmentNode.name,
|
299
|
-
EDGES,
|
300
|
-
PAGE_INFO,
|
301
|
-
connection,
|
302
|
-
);
|
303
|
-
|
304
|
-
const edges = connection[EDGES];
|
305
|
-
const pageInfo = connection[PAGE_INFO];
|
306
|
-
if (edges == null || pageInfo == null) {
|
307
|
-
return {cursor: null, hasMore: false};
|
308
|
-
}
|
309
|
-
|
310
|
-
invariant(
|
311
|
-
Array.isArray(edges),
|
312
|
-
'Relay: Expected connection in fragment `%s` to have a plural `%s` field. ' +
|
313
|
-
'Instead got `%s`.',
|
314
|
-
fragmentNode.name,
|
315
|
-
EDGES,
|
316
|
-
edges,
|
317
|
-
);
|
318
|
-
invariant(
|
319
|
-
typeof pageInfo === 'object',
|
320
|
-
'Relay: Expected connection in fragment `%s` to have a `%s` field. ' +
|
321
|
-
'Instead got `%s`.',
|
322
|
-
fragmentNode.name,
|
323
|
-
PAGE_INFO,
|
324
|
-
pageInfo,
|
325
|
-
);
|
326
|
-
|
327
|
-
const cursor =
|
328
|
-
direction === 'forward'
|
329
|
-
? pageInfo[END_CURSOR] ?? null
|
330
|
-
: pageInfo[START_CURSOR] ?? null;
|
331
|
-
invariant(
|
332
|
-
cursor === null || typeof cursor === 'string',
|
333
|
-
'Relay: Expected page info for connection in fragment `%s` to have a ' +
|
334
|
-
'valid `%s`. Instead got `%s`.',
|
335
|
-
fragmentNode.name,
|
336
|
-
START_CURSOR,
|
337
|
-
cursor,
|
338
|
-
);
|
339
|
-
|
340
|
-
let hasMore;
|
341
|
-
if (direction === 'forward') {
|
342
|
-
hasMore = cursor != null && pageInfo[HAS_NEXT_PAGE] === true;
|
343
|
-
} else {
|
344
|
-
hasMore = cursor != null && pageInfo[HAS_PREV_PAGE] === true;
|
345
|
-
}
|
346
|
-
|
347
|
-
return {cursor, hasMore};
|
348
|
-
}
|
349
|
-
|
350
284
|
module.exports = useLoadMoreFunction;
|
@@ -0,0 +1,280 @@
|
|
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 {
|
15
|
+
ConcreteRequest,
|
16
|
+
Direction,
|
17
|
+
Disposable,
|
18
|
+
GraphQLResponse,
|
19
|
+
Observer,
|
20
|
+
ReaderFragment,
|
21
|
+
ReaderPaginationMetadata,
|
22
|
+
Subscription,
|
23
|
+
Variables,
|
24
|
+
} from 'relay-runtime';
|
25
|
+
|
26
|
+
const getConnectionState = require('./getConnectionState');
|
27
|
+
const useIsMountedRef = require('./useIsMountedRef');
|
28
|
+
const useIsOperationNodeActive = require('./useIsOperationNodeActive');
|
29
|
+
const useRelayEnvironment = require('./useRelayEnvironment');
|
30
|
+
const invariant = require('invariant');
|
31
|
+
const {useCallback, useRef, useState} = require('react');
|
32
|
+
const {
|
33
|
+
__internal: {fetchQuery},
|
34
|
+
createOperationDescriptor,
|
35
|
+
getPaginationVariables,
|
36
|
+
getRefetchMetadata,
|
37
|
+
getSelector,
|
38
|
+
} = require('relay-runtime');
|
39
|
+
const warning = require('warning');
|
40
|
+
|
41
|
+
export type LoadMoreFn<TVariables: Variables> = (
|
42
|
+
count: number,
|
43
|
+
options?: {
|
44
|
+
onComplete?: (Error | null) => void,
|
45
|
+
UNSTABLE_extraVariables?: Partial<TVariables>,
|
46
|
+
},
|
47
|
+
) => Disposable;
|
48
|
+
|
49
|
+
export type UseLoadMoreFunctionArgs = {
|
50
|
+
direction: Direction,
|
51
|
+
fragmentNode: ReaderFragment,
|
52
|
+
fragmentRef: mixed,
|
53
|
+
fragmentIdentifier: string,
|
54
|
+
fragmentData: mixed,
|
55
|
+
connectionPathInFragmentData: $ReadOnlyArray<string | number>,
|
56
|
+
paginationRequest: ConcreteRequest,
|
57
|
+
paginationMetadata: ReaderPaginationMetadata,
|
58
|
+
componentDisplayName: string,
|
59
|
+
observer: Observer<GraphQLResponse>,
|
60
|
+
onReset: () => void,
|
61
|
+
};
|
62
|
+
|
63
|
+
hook useLoadMoreFunction_EXPERIMENTAL<TVariables: Variables>(
|
64
|
+
args: UseLoadMoreFunctionArgs,
|
65
|
+
): [
|
66
|
+
// Function to load more data
|
67
|
+
LoadMoreFn<TVariables>,
|
68
|
+
// Whether the connection has more data to load
|
69
|
+
boolean,
|
70
|
+
// Force dispose function which cancels the in-flight fetch itself, and callbacks
|
71
|
+
() => void,
|
72
|
+
] {
|
73
|
+
const {
|
74
|
+
direction,
|
75
|
+
fragmentNode,
|
76
|
+
fragmentRef,
|
77
|
+
fragmentIdentifier,
|
78
|
+
fragmentData,
|
79
|
+
connectionPathInFragmentData,
|
80
|
+
paginationRequest,
|
81
|
+
paginationMetadata,
|
82
|
+
componentDisplayName,
|
83
|
+
observer,
|
84
|
+
onReset,
|
85
|
+
} = args;
|
86
|
+
const environment = useRelayEnvironment();
|
87
|
+
|
88
|
+
const {identifierInfo} = getRefetchMetadata(
|
89
|
+
fragmentNode,
|
90
|
+
componentDisplayName,
|
91
|
+
);
|
92
|
+
const identifierValue =
|
93
|
+
identifierInfo?.identifierField != null &&
|
94
|
+
fragmentData != null &&
|
95
|
+
typeof fragmentData === 'object'
|
96
|
+
? fragmentData[identifierInfo.identifierField]
|
97
|
+
: null;
|
98
|
+
|
99
|
+
const fetchStatusRef = useRef<
|
100
|
+
{kind: 'fetching', subscription: Subscription} | {kind: 'none'},
|
101
|
+
>({kind: 'none'});
|
102
|
+
const [mirroredEnvironment, setMirroredEnvironment] = useState(environment);
|
103
|
+
const [mirroredFragmentIdentifier, setMirroredFragmentIdentifier] =
|
104
|
+
useState(fragmentIdentifier);
|
105
|
+
|
106
|
+
const isParentQueryActive = useIsOperationNodeActive(
|
107
|
+
fragmentNode,
|
108
|
+
fragmentRef,
|
109
|
+
);
|
110
|
+
|
111
|
+
const forceDisposeFn = useCallback(() => {
|
112
|
+
// $FlowFixMe[react-rule-unsafe-ref]
|
113
|
+
if (fetchStatusRef.current.kind === 'fetching') {
|
114
|
+
// $FlowFixMe[react-rule-unsafe-ref]
|
115
|
+
fetchStatusRef.current.subscription.unsubscribe();
|
116
|
+
}
|
117
|
+
// $FlowFixMe[react-rule-unsafe-ref]
|
118
|
+
fetchStatusRef.current = {kind: 'none'};
|
119
|
+
}, []);
|
120
|
+
|
121
|
+
const shouldReset =
|
122
|
+
environment !== mirroredEnvironment ||
|
123
|
+
fragmentIdentifier !== mirroredFragmentIdentifier;
|
124
|
+
if (shouldReset) {
|
125
|
+
forceDisposeFn();
|
126
|
+
onReset();
|
127
|
+
setMirroredEnvironment(environment);
|
128
|
+
setMirroredFragmentIdentifier(fragmentIdentifier);
|
129
|
+
}
|
130
|
+
|
131
|
+
const {cursor, hasMore} = getConnectionState(
|
132
|
+
direction,
|
133
|
+
fragmentNode,
|
134
|
+
fragmentData,
|
135
|
+
connectionPathInFragmentData,
|
136
|
+
);
|
137
|
+
|
138
|
+
const isMountedRef = useIsMountedRef();
|
139
|
+
const loadMore = useCallback(
|
140
|
+
(
|
141
|
+
count: number,
|
142
|
+
options: void | {
|
143
|
+
UNSTABLE_extraVariables?: Partial<TVariables>,
|
144
|
+
onComplete?: (Error | null) => void,
|
145
|
+
},
|
146
|
+
) => {
|
147
|
+
// TODO(T41131846): Fetch/Caching policies for loadMore
|
148
|
+
|
149
|
+
const onComplete = options?.onComplete;
|
150
|
+
if (isMountedRef.current !== true) {
|
151
|
+
// Bail out and warn if we're trying to paginate after the component
|
152
|
+
// has unmounted
|
153
|
+
warning(
|
154
|
+
false,
|
155
|
+
'Relay: Unexpected fetch on unmounted component for fragment ' +
|
156
|
+
'`%s` in `%s`. It looks like some instances of your component are ' +
|
157
|
+
'still trying to fetch data but they already unmounted. ' +
|
158
|
+
'Please make sure you clear all timers, intervals, ' +
|
159
|
+
'async calls, etc that may trigger a fetch.',
|
160
|
+
fragmentNode.name,
|
161
|
+
componentDisplayName,
|
162
|
+
);
|
163
|
+
return {dispose: () => {}};
|
164
|
+
}
|
165
|
+
|
166
|
+
const fragmentSelector = getSelector(fragmentNode, fragmentRef);
|
167
|
+
if (
|
168
|
+
fetchStatusRef.current.kind === 'fetching' ||
|
169
|
+
fragmentData == null ||
|
170
|
+
isParentQueryActive
|
171
|
+
) {
|
172
|
+
if (fragmentSelector == null) {
|
173
|
+
warning(
|
174
|
+
false,
|
175
|
+
'Relay: Unexpected fetch while using a null fragment ref ' +
|
176
|
+
'for fragment `%s` in `%s`. When fetching more items, we expect ' +
|
177
|
+
"initial fragment data to be non-null. Please make sure you're " +
|
178
|
+
'passing a valid fragment ref to `%s` before paginating.',
|
179
|
+
fragmentNode.name,
|
180
|
+
componentDisplayName,
|
181
|
+
componentDisplayName,
|
182
|
+
);
|
183
|
+
}
|
184
|
+
|
185
|
+
if (onComplete) {
|
186
|
+
onComplete(null);
|
187
|
+
}
|
188
|
+
return {dispose: () => {}};
|
189
|
+
}
|
190
|
+
|
191
|
+
invariant(
|
192
|
+
fragmentSelector != null &&
|
193
|
+
fragmentSelector.kind !== 'PluralReaderSelector',
|
194
|
+
'Relay: Expected to be able to find a non-plural fragment owner for ' +
|
195
|
+
"fragment `%s` when using `%s`. If you're seeing this, " +
|
196
|
+
'this is likely a bug in Relay.',
|
197
|
+
fragmentNode.name,
|
198
|
+
componentDisplayName,
|
199
|
+
);
|
200
|
+
|
201
|
+
const parentVariables = fragmentSelector.owner.variables;
|
202
|
+
const fragmentVariables = fragmentSelector.variables;
|
203
|
+
const extraVariables = options?.UNSTABLE_extraVariables;
|
204
|
+
const baseVariables = {
|
205
|
+
...parentVariables,
|
206
|
+
...fragmentVariables,
|
207
|
+
};
|
208
|
+
const paginationVariables = getPaginationVariables(
|
209
|
+
direction,
|
210
|
+
count,
|
211
|
+
cursor,
|
212
|
+
baseVariables,
|
213
|
+
{...extraVariables},
|
214
|
+
paginationMetadata,
|
215
|
+
);
|
216
|
+
|
217
|
+
// If the query needs an identifier value ('id' or similar) and one
|
218
|
+
// was not explicitly provided, read it from the fragment data.
|
219
|
+
if (identifierInfo != null) {
|
220
|
+
// @refetchable fragments are guaranteed to have an `id` selection
|
221
|
+
// if the type is Node, implements Node, or is @fetchable. Double-check
|
222
|
+
// that there actually is a value at runtime.
|
223
|
+
if (typeof identifierValue !== 'string') {
|
224
|
+
warning(
|
225
|
+
false,
|
226
|
+
'Relay: Expected result to have a string ' +
|
227
|
+
'`%s` in order to refetch, got `%s`.',
|
228
|
+
identifierInfo.identifierField,
|
229
|
+
identifierValue,
|
230
|
+
);
|
231
|
+
}
|
232
|
+
paginationVariables[identifierInfo.identifierQueryVariableName] =
|
233
|
+
identifierValue;
|
234
|
+
}
|
235
|
+
|
236
|
+
const paginationQuery = createOperationDescriptor(
|
237
|
+
paginationRequest,
|
238
|
+
paginationVariables,
|
239
|
+
{force: true},
|
240
|
+
);
|
241
|
+
fetchQuery(environment, paginationQuery).subscribe({
|
242
|
+
...observer,
|
243
|
+
start: subscription => {
|
244
|
+
fetchStatusRef.current = {kind: 'fetching', subscription};
|
245
|
+
observer.start && observer.start(subscription);
|
246
|
+
},
|
247
|
+
complete: () => {
|
248
|
+
fetchStatusRef.current = {kind: 'none'};
|
249
|
+
observer.complete && observer.complete();
|
250
|
+
onComplete && onComplete(null);
|
251
|
+
},
|
252
|
+
error: error => {
|
253
|
+
fetchStatusRef.current = {kind: 'none'};
|
254
|
+
observer.complete && observer.complete();
|
255
|
+
onComplete && onComplete(error);
|
256
|
+
},
|
257
|
+
});
|
258
|
+
return {
|
259
|
+
dispose: () => {},
|
260
|
+
};
|
261
|
+
},
|
262
|
+
// NOTE: We disable react-hooks-deps warning because all values
|
263
|
+
// inside paginationMetadata are static
|
264
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
265
|
+
[
|
266
|
+
environment,
|
267
|
+
identifierValue,
|
268
|
+
direction,
|
269
|
+
cursor,
|
270
|
+
isParentQueryActive,
|
271
|
+
fragmentData,
|
272
|
+
fragmentNode.name,
|
273
|
+
fragmentRef,
|
274
|
+
componentDisplayName,
|
275
|
+
],
|
276
|
+
);
|
277
|
+
return [loadMore, hasMore, forceDisposeFn];
|
278
|
+
}
|
279
|
+
|
280
|
+
module.exports = useLoadMoreFunction_EXPERIMENTAL;
|
@@ -25,9 +25,10 @@ import type {
|
|
25
25
|
|
26
26
|
const {loadQuery} = require('./loadQuery');
|
27
27
|
const useIsMountedRef = require('./useIsMountedRef');
|
28
|
+
const useQueryLoader_EXPERIMENTAL = require('./useQueryLoader_EXPERIMENTAL');
|
28
29
|
const useRelayEnvironment = require('./useRelayEnvironment');
|
29
30
|
const {useCallback, useEffect, useRef, useState} = require('react');
|
30
|
-
const {getRequest} = require('relay-runtime');
|
31
|
+
const {RelayFeatureFlags, getRequest} = require('relay-runtime');
|
31
32
|
|
32
33
|
export type LoaderFn<TQuery: OperationType> = (
|
33
34
|
variables: TQuery['variables'],
|
@@ -42,7 +43,7 @@ export type UseQueryLoaderLoadQueryOptions = $ReadOnly<{
|
|
42
43
|
// NullQueryReference needs to implement referential equality,
|
43
44
|
// so that multiple NullQueryReferences can be in the same set
|
44
45
|
// (corresponding to multiple calls to disposeQuery).
|
45
|
-
type NullQueryReference = {
|
46
|
+
export type NullQueryReference = {
|
46
47
|
kind: 'NullQueryReference',
|
47
48
|
};
|
48
49
|
const initialNullQueryReferenceState = {kind: 'NullQueryReference'};
|
@@ -68,7 +69,7 @@ function requestIsLiveQuery<
|
|
68
69
|
return request.params.metadata.live !== undefined;
|
69
70
|
}
|
70
71
|
|
71
|
-
type UseQueryLoaderHookReturnType<
|
72
|
+
export type UseQueryLoaderHookReturnType<
|
72
73
|
TVariables: Variables,
|
73
74
|
TData,
|
74
75
|
TRawResponse: ?{...} = void,
|
@@ -115,6 +116,29 @@ hook useQueryLoader<TVariables: Variables, TData, TRawResponse: ?{...} = void>(
|
|
115
116
|
variables: TVariables,
|
116
117
|
rawResponse?: $NonMaybeType<TRawResponse>,
|
117
118
|
}>,
|
119
|
+
): UseQueryLoaderHookReturnType<TVariables, TData> {
|
120
|
+
if (RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY) {
|
121
|
+
// $FlowFixMe[react-rule-hook] - the condition is static
|
122
|
+
return useQueryLoader_EXPERIMENTAL(
|
123
|
+
preloadableRequest,
|
124
|
+
initialQueryReference,
|
125
|
+
);
|
126
|
+
}
|
127
|
+
// $FlowFixMe[react-rule-hook] - the condition is static
|
128
|
+
return useQueryLoader_CURRENT(preloadableRequest, initialQueryReference);
|
129
|
+
}
|
130
|
+
|
131
|
+
hook useQueryLoader_CURRENT<
|
132
|
+
TVariables: Variables,
|
133
|
+
TData,
|
134
|
+
TRawResponse: ?{...} = void,
|
135
|
+
>(
|
136
|
+
preloadableRequest: Query<TVariables, TData, TRawResponse>,
|
137
|
+
initialQueryReference?: ?PreloadedQuery<{
|
138
|
+
response: TData,
|
139
|
+
variables: TVariables,
|
140
|
+
rawResponse?: $NonMaybeType<TRawResponse>,
|
141
|
+
}>,
|
118
142
|
): UseQueryLoaderHookReturnType<TVariables, TData> {
|
119
143
|
type QueryType = {
|
120
144
|
response: TData,
|