react-relay 19.0.0 → 20.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/ReactRelayPaginationContainer.js.flow +1 -1
- package/ReactRelayRefetchContainer.js.flow +1 -1
- package/buildReactRelayContainer.js.flow +1 -2
- package/getRootVariablesForFragments.js.flow +2 -0
- package/hooks.js +1 -1
- package/index.js +1 -1
- package/legacy.js +1 -1
- package/lib/relay-hooks/legacy/FragmentResource.js +1 -1
- package/lib/relay-hooks/readFragmentInternal.js +2 -2
- package/lib/relay-hooks/useFragmentInternal_CURRENT.js +2 -2
- package/lib/relay-hooks/useFragmentInternal_EXPERIMENTAL.js +15 -5
- package/package.json +2 -2
- package/relay-hooks/EntryPointTypes.flow.js.flow +3 -2
- package/relay-hooks/RelayEnvironmentProvider.js.flow +5 -3
- package/relay-hooks/legacy/FragmentResource.js.flow +3 -2
- package/relay-hooks/loadQuery.js.flow +8 -2
- package/relay-hooks/readFragmentInternal.js.flow +7 -3
- package/relay-hooks/useEntryPointLoader.js.flow +1 -1
- package/relay-hooks/useFragmentInternal_CURRENT.js.flow +8 -3
- package/relay-hooks/useFragmentInternal_EXPERIMENTAL.js.flow +33 -12
- package/relay-hooks/useLazyLoadQuery.js.flow +63 -14
- package/relay-hooks/useLazyLoadQueryNode.js.flow +1 -1
- package/relay-hooks/usePreloadedQuery.js.flow +7 -3
- package/relay-hooks/useQueryLoader.js.flow +1 -1
- package/relay-hooks/useRelayEnvironment.js.flow +22 -0
package/ReactRelayContext.js
CHANGED
@@ -805,7 +805,7 @@ function createContainerWithFragments<
|
|
805
805
|
...fragmentVariables,
|
806
806
|
}: Variables);
|
807
807
|
|
808
|
-
const cacheConfig: ?CacheConfig = options
|
808
|
+
const cacheConfig: ?{...CacheConfig} = options
|
809
809
|
? {force: !!options.force}
|
810
810
|
: undefined;
|
811
811
|
if (cacheConfig != null && options?.metadata != null) {
|
@@ -339,7 +339,7 @@ function createContainerWithFragments<
|
|
339
339
|
? {...fetchVariables, ...renderVariables}
|
340
340
|
: fetchVariables;
|
341
341
|
|
342
|
-
const cacheConfig: ?CacheConfig = options
|
342
|
+
const cacheConfig: ?{...CacheConfig} = options
|
343
343
|
? {force: !!options.force}
|
344
344
|
: undefined;
|
345
345
|
if (cacheConfig != null && options?.metadata != null) {
|
@@ -82,8 +82,7 @@ function buildReactRelayContainer<TBase: component(...empty)>(
|
|
82
82
|
);
|
83
83
|
}
|
84
84
|
ForwardRef.displayName = containerName;
|
85
|
-
|
86
|
-
const ForwardContainer = React.forwardRef(ForwardRef);
|
85
|
+
const ForwardContainer = (React as $FlowFixMe).forwardRef(ForwardRef);
|
87
86
|
|
88
87
|
if (__DEV__) {
|
89
88
|
// Used by RelayModernTestUtils
|
@@ -32,6 +32,8 @@ function getRootVariablesForFragments<TProps: {...}>(
|
|
32
32
|
selector != null && selector.kind === 'PluralReaderSelector'
|
33
33
|
? selector.selectors[0]?.owner.variables ?? {}
|
34
34
|
: selector?.owner.variables ?? {};
|
35
|
+
/* $FlowFixMe[incompatible-indexer] Natural Inference rollout. See
|
36
|
+
* https://fburl.com/gdoc/y8dn025u */
|
35
37
|
rootVariables = {
|
36
38
|
...rootVariables,
|
37
39
|
...fragmentOwnerVariables,
|
package/hooks.js
CHANGED
package/index.js
CHANGED
package/legacy.js
CHANGED
@@ -193,7 +193,7 @@ var FragmentResourceImpl = /*#__PURE__*/function () {
|
|
193
193
|
}
|
194
194
|
}
|
195
195
|
var fragmentSelector = getSelector(fragmentNode, fragmentRef);
|
196
|
-
!(fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + '
|
196
|
+
!(fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, componentDisplayName, fragmentNode.name, fragmentKey == null ? 'a fragment reference' : "the `".concat(fragmentKey, "`"), componentDisplayName, fragmentNode.name) : invariant(false) : void 0;
|
197
197
|
var fragmentResult = null;
|
198
198
|
var snapshot = null;
|
199
199
|
if (RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE && cachedValue != null && cachedValue.kind === 'missing') {
|
@@ -127,10 +127,10 @@ function readFragmentInternal(environment, fragmentNode, fragmentRef, hookDispla
|
|
127
127
|
} else {
|
128
128
|
!!Array.isArray(fragmentRef) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected fragment pointer%s for fragment `%s` not to be ' + 'an array, instead got `%s`. Add `@relay(plural: true)` ' + 'to fragment `%s` to allow the prop to be an array.', fragmentKey != null ? " for key `".concat(fragmentKey, "`") : '', fragmentNode.name, typeof fragmentRef, fragmentNode.name) : invariant(false) : void 0;
|
129
129
|
}
|
130
|
-
!(fragmentRef == null || isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0 || fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + '
|
130
|
+
!(fragmentRef == null || isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0 || fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, fragmentKey == null ? 'a fragment reference' : "the `".concat(fragmentKey, "`"), hookDisplayName, fragmentNode.name) : invariant(false) : void 0;
|
131
131
|
var state = getFragmentState(environment, fragmentSelector);
|
132
132
|
var clientEdgeQueries = null;
|
133
|
-
if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true) {
|
133
|
+
if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true || RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES) {
|
134
134
|
var missingClientEdges = getMissingClientEdges(state);
|
135
135
|
if (missingClientEdges !== null && missingClientEdges !== void 0 && missingClientEdges.length) {
|
136
136
|
clientEdgeQueries = [];
|
@@ -308,7 +308,7 @@ function useFragmentInternal(fragmentNode, fragmentRef, hookDisplayName, queryOp
|
|
308
308
|
} else {
|
309
309
|
!!Array.isArray(fragmentRef) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected fragment pointer%s for fragment `%s` not to be ' + 'an array, instead got `%s`. Add `@relay(plural: true)` ' + 'to fragment `%s` to allow the prop to be an array.', fragmentNode.name, typeof fragmentRef, fragmentNode.name) : invariant(false) : void 0;
|
310
310
|
}
|
311
|
-
!(fragmentRef == null || isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0 || fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + '
|
311
|
+
!(fragmentRef == null || isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0 || fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, hookDisplayName, fragmentNode.name) : invariant(false) : void 0;
|
312
312
|
var environment = useRelayEnvironment();
|
313
313
|
var loggerContext;
|
314
314
|
if (RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER) {
|
@@ -343,7 +343,7 @@ function useFragmentInternal(fragmentNode, fragmentRef, hookDisplayName, queryOp
|
|
343
343
|
useEffect(function () {
|
344
344
|
committedFragmentSelectorRef.current = fragmentSelector;
|
345
345
|
}, [fragmentSelector]);
|
346
|
-
if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true) {
|
346
|
+
if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true || RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES) {
|
347
347
|
var _useMemo = useMemo(function () {
|
348
348
|
var missingClientEdges = getMissingClientEdges(state);
|
349
349
|
var clientEdgeQueries;
|
@@ -199,7 +199,11 @@ function handleMissingClientEdge(environment, parentFragmentNode, parentFragment
|
|
199
199
|
var queryOperationDescriptor = createOperationDescriptor(missingClientEdgeRequestInfo.request, variables, queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.networkCacheConfig);
|
200
200
|
var QueryResource = getQueryResourceForEnvironment(environment);
|
201
201
|
var queryResult = QueryResource.prepare(queryOperationDescriptor, fetchQueryInternal(environment, queryOperationDescriptor), queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.fetchPolicy);
|
202
|
-
|
202
|
+
var promise = getPromiseForActiveRequest(environment, queryOperationDescriptor.request);
|
203
|
+
if (promise != null && promise.displayName == null) {
|
204
|
+
promise.displayName = missingClientEdgeRequestInfo.request.params.name;
|
205
|
+
}
|
206
|
+
return [queryResult, promise];
|
203
207
|
}
|
204
208
|
function subscribeToSnapshot(environment, state, setState) {
|
205
209
|
if (state.kind === 'bailout') {
|
@@ -323,7 +327,7 @@ function useFragmentInternal_EXPERIMENTAL(fragmentNode, fragmentRef, hookDisplay
|
|
323
327
|
} else {
|
324
328
|
!!Array.isArray(fragmentRef) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected fragment pointer%s for fragment `%s` not to be ' + 'an array, instead got `%s`. Add `@relay(plural: true)` ' + 'to fragment `%s` to allow the prop to be an array.', fragmentNode.name, typeof fragmentRef, fragmentNode.name) : invariant(false) : void 0;
|
325
329
|
}
|
326
|
-
!(fragmentRef == null || isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0 || fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread
|
330
|
+
!(fragmentRef == null || isPlural && Array.isArray(fragmentRef) && fragmentRef.length === 0 || fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected to receive an object where `...%s` was spread, ' + 'but the fragment reference was not found`. This is most ' + 'likely the result of:\n' + "- Forgetting to spread `%s` in `%s`'s parent's fragment.\n" + '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, hookDisplayName) : invariant(false) : void 0;
|
327
331
|
var environment = useRelayEnvironment();
|
328
332
|
var loggerContext;
|
329
333
|
if (RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER) {
|
@@ -345,7 +349,7 @@ function useFragmentInternal_EXPERIMENTAL(fragmentNode, fragmentRef, hookDisplay
|
|
345
349
|
useEffect(function () {
|
346
350
|
committedFragmentSelectorRef.current = fragmentSelector;
|
347
351
|
}, [fragmentSelector]);
|
348
|
-
if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true) {
|
352
|
+
if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true || RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES) {
|
349
353
|
var _useMemo = useMemo(function () {
|
350
354
|
var missingClientEdges = getMissingClientEdges(state);
|
351
355
|
var clientEdgeQueries;
|
@@ -376,7 +380,11 @@ function useFragmentInternal_EXPERIMENTAL(fragmentNode, fragmentRef, hookDisplay
|
|
376
380
|
clientEdgeQueries = _useMemo[0],
|
377
381
|
activeRequestPromises = _useMemo[1];
|
378
382
|
if (activeRequestPromises.length) {
|
379
|
-
|
383
|
+
var allPromises = Promise.all(activeRequestPromises);
|
384
|
+
allPromises.displayName = "RelayClientEdge(".concat(activeRequestPromises.map(function (promise) {
|
385
|
+
return promise.displayName;
|
386
|
+
}).join(','), ")");
|
387
|
+
throw allPromises;
|
380
388
|
}
|
381
389
|
useEffect(function () {
|
382
390
|
var QueryResource = getQueryResourceForEnvironment(environment);
|
@@ -406,9 +414,11 @@ function useFragmentInternal_EXPERIMENTAL(fragmentNode, fragmentRef, hookDisplay
|
|
406
414
|
if (isMissingData(state)) {
|
407
415
|
var suspendingLiveResolvers = getSuspendingLiveResolver(state);
|
408
416
|
if (suspendingLiveResolvers != null && suspendingLiveResolvers.length > 0) {
|
409
|
-
|
417
|
+
var promise = Promise.all(suspendingLiveResolvers.map(function (liveStateID) {
|
410
418
|
return environment.getStore().getLiveResolverPromise(liveStateID);
|
411
419
|
}));
|
420
|
+
promise.displayName = 'RelayLiveResolver(' + fragmentNode.name + ')';
|
421
|
+
throw promise;
|
412
422
|
}
|
413
423
|
if (RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE || environment !== previousEnvironment || !committedFragmentSelectorRef.current || !areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector)) {
|
414
424
|
!(fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'refinement, see invariants above') : invariant(false) : void 0;
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "react-relay",
|
3
3
|
"description": "A framework for building GraphQL-driven React applications.",
|
4
|
-
"version": "
|
4
|
+
"version": "20.1.0",
|
5
5
|
"keywords": [
|
6
6
|
"graphql",
|
7
7
|
"relay",
|
@@ -20,7 +20,7 @@
|
|
20
20
|
"fbjs": "^3.0.2",
|
21
21
|
"invariant": "^2.2.4",
|
22
22
|
"nullthrows": "^1.1.1",
|
23
|
-
"relay-runtime": "
|
23
|
+
"relay-runtime": "20.1.0"
|
24
24
|
},
|
25
25
|
"peerDependencies": {
|
26
26
|
"react": "^16.9.0 || ^17 || ^18 || ^19"
|
@@ -37,6 +37,7 @@ export type PreloadOptions = {
|
|
37
37
|
+fetchKey?: string | number,
|
38
38
|
+fetchPolicy?: ?PreloadFetchPolicy,
|
39
39
|
+includeIf?: ?boolean,
|
40
|
+
+prefetchExpiryInHours?: ?number,
|
40
41
|
+networkCacheConfig?: ?CacheConfig,
|
41
42
|
};
|
42
43
|
|
@@ -292,6 +293,6 @@ export type EntryPoint<TEntryPointParams, +TEntryPointComponent> =
|
|
292
293
|
|
293
294
|
export type PreloadParamsOf<T> = Parameters<T['getPreloadProps']>[0];
|
294
295
|
|
295
|
-
export type IEnvironmentProvider<TOptions> = {
|
296
|
+
export type IEnvironmentProvider<TOptions> = $ReadOnly<{
|
296
297
|
getEnvironment: (options: ?TOptions) => IEnvironment,
|
297
|
-
}
|
298
|
+
}>;
|
@@ -22,15 +22,17 @@ const React = require('react');
|
|
22
22
|
|
23
23
|
const {useMemo} = React;
|
24
24
|
|
25
|
-
type Props = $ReadOnly<{
|
26
|
-
children:
|
25
|
+
type Props<TChildren> = $ReadOnly<{
|
26
|
+
children: TChildren,
|
27
27
|
environment: IEnvironment,
|
28
28
|
getEnvironmentForActor?: ?(
|
29
29
|
actorIdentifier: ActorIdentifier,
|
30
30
|
) => IActorEnvironment,
|
31
31
|
}>;
|
32
32
|
|
33
|
-
|
33
|
+
component RelayEnvironmentProvider<TChildren: React.Node>(
|
34
|
+
...props: Props<TChildren>
|
35
|
+
) renders TChildren {
|
34
36
|
const {children, environment, getEnvironmentForActor} = props;
|
35
37
|
const context = useMemo(
|
36
38
|
() => ({environment, getEnvironmentForActor}),
|
@@ -340,14 +340,15 @@ class FragmentResourceImpl {
|
|
340
340
|
'to `%s`. If the parent fragment only fetches the fragment conditionally ' +
|
341
341
|
'- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' +
|
342
342
|
'spread - then the fragment reference will not exist. ' +
|
343
|
-
'
|
344
|
-
'
|
343
|
+
'This issue can generally be fixed by adding `@alias` after `...%s`.\n' +
|
344
|
+
'See https://relay.dev/docs/next/guides/alias-directive/',
|
345
345
|
fragmentNode.name,
|
346
346
|
fragmentNode.name,
|
347
347
|
componentDisplayName,
|
348
348
|
fragmentNode.name,
|
349
349
|
fragmentKey == null ? 'a fragment reference' : `the \`${fragmentKey}\``,
|
350
350
|
componentDisplayName,
|
351
|
+
fragmentNode.name,
|
351
352
|
);
|
352
353
|
|
353
354
|
let fragmentResult = null;
|
@@ -43,7 +43,7 @@ const {
|
|
43
43
|
|
44
44
|
let fetchKey = 100001;
|
45
45
|
|
46
|
-
type QueryType<T> =
|
46
|
+
export type QueryType<T> =
|
47
47
|
T extends Query<infer V, infer D, infer RR>
|
48
48
|
? {
|
49
49
|
variables: V,
|
@@ -126,7 +126,7 @@ function loadQuery<
|
|
126
126
|
let networkError = null;
|
127
127
|
// makeNetworkRequest will immediately start a raw network request if
|
128
128
|
// one isn't already in flight and return an Observable that when
|
129
|
-
// subscribed to will replay the network events that have
|
129
|
+
// subscribed to will replay the network events that have occurred so far,
|
130
130
|
// as well as subsequent events.
|
131
131
|
let didMakeNetworkRequest = false;
|
132
132
|
const makeNetworkRequest = (
|
@@ -351,10 +351,16 @@ function loadQuery<
|
|
351
351
|
return;
|
352
352
|
}
|
353
353
|
if (didExecuteNetworkSource) {
|
354
|
+
/* $FlowFixMe[constant-condition] Error discovered during Constant
|
355
|
+
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
|
354
356
|
unsubscribeFromExecution && unsubscribeFromExecution();
|
355
357
|
} else {
|
358
|
+
/* $FlowFixMe[constant-condition] Error discovered during Constant
|
359
|
+
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
|
356
360
|
unsubscribeFromNetworkRequest && unsubscribeFromNetworkRequest();
|
357
361
|
}
|
362
|
+
/* $FlowFixMe[constant-condition] Error discovered during Constant
|
363
|
+
* Condition roll out. See https://fburl.com/workplace/1v97vimq. */
|
358
364
|
cancelOnLoadCallback && cancelOnLoadCallback();
|
359
365
|
isNetworkRequestCancelled = true;
|
360
366
|
};
|
@@ -198,14 +198,15 @@ function readFragmentInternal(
|
|
198
198
|
'to `%s`. If the parent fragment only fetches the fragment conditionally ' +
|
199
199
|
'- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' +
|
200
200
|
'spread - then the fragment reference will not exist. ' +
|
201
|
-
'
|
202
|
-
'
|
201
|
+
'This issue can generally be fixed by adding `@alias` after `...%s`.\n' +
|
202
|
+
'See https://relay.dev/docs/next/guides/alias-directive/',
|
203
203
|
fragmentNode.name,
|
204
204
|
fragmentNode.name,
|
205
205
|
hookDisplayName,
|
206
206
|
fragmentNode.name,
|
207
207
|
fragmentKey == null ? 'a fragment reference' : `the \`${fragmentKey}\``,
|
208
208
|
hookDisplayName,
|
209
|
+
fragmentNode.name,
|
209
210
|
);
|
210
211
|
|
211
212
|
const state = getFragmentState(environment, fragmentSelector);
|
@@ -213,7 +214,10 @@ function readFragmentInternal(
|
|
213
214
|
// Handle the queries for any missing client edges; this may suspend.
|
214
215
|
// FIXME handle client edges in parallel.
|
215
216
|
let clientEdgeQueries = null;
|
216
|
-
if (
|
217
|
+
if (
|
218
|
+
fragmentNode.metadata?.hasClientEdges === true ||
|
219
|
+
RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES
|
220
|
+
) {
|
217
221
|
const missingClientEdges = getMissingClientEdges(state);
|
218
222
|
if (missingClientEdges?.length) {
|
219
223
|
clientEdgeQueries = ([]: Array<QueryResult>);
|
@@ -158,7 +158,7 @@ hook useLoadEntryPoint<
|
|
158
158
|
useEffect(() => {
|
159
159
|
return () => {
|
160
160
|
// Attempt to detect if the component was
|
161
|
-
// hidden (by Offscreen API), or fast refresh
|
161
|
+
// hidden (by Offscreen API), or fast refresh occurred;
|
162
162
|
// Only in these situations would the effect cleanup
|
163
163
|
// for "unmounting" run multiple times, so if
|
164
164
|
// we are ever able to read this ref with a value
|
@@ -400,13 +400,14 @@ hook useFragmentInternal(
|
|
400
400
|
'to `%s`. If the parent fragment only fetches the fragment conditionally ' +
|
401
401
|
'- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' +
|
402
402
|
'spread - then the fragment reference will not exist. ' +
|
403
|
-
'
|
404
|
-
'
|
403
|
+
'This issue can generally be fixed by adding `@alias` after `...%s`.\n' +
|
404
|
+
'See https://relay.dev/docs/next/guides/alias-directive/',
|
405
405
|
fragmentNode.name,
|
406
406
|
fragmentNode.name,
|
407
407
|
hookDisplayName,
|
408
408
|
fragmentNode.name,
|
409
409
|
hookDisplayName,
|
410
|
+
fragmentNode.name,
|
410
411
|
);
|
411
412
|
|
412
413
|
const environment = useRelayEnvironment();
|
@@ -461,7 +462,10 @@ hook useFragmentInternal(
|
|
461
462
|
|
462
463
|
// Handle the queries for any missing client edges; this may suspend.
|
463
464
|
// FIXME handle client edges in parallel.
|
464
|
-
if (
|
465
|
+
if (
|
466
|
+
fragmentNode.metadata?.hasClientEdges === true ||
|
467
|
+
RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES
|
468
|
+
) {
|
465
469
|
// The fragment is validated to be static (in useFragment) and hasClientEdges is
|
466
470
|
// a static (constant) property of the fragment. In practice, this effect will
|
467
471
|
// always or never run for a given invocation of this hook.
|
@@ -534,6 +538,7 @@ hook useFragmentInternal(
|
|
534
538
|
if (
|
535
539
|
RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE ||
|
536
540
|
environment !== previousEnvironment ||
|
541
|
+
// $FlowFixMe[sketchy-null-bool]
|
537
542
|
!committedFragmentSelectorRef.current ||
|
538
543
|
// $FlowFixMe[react-rule-unsafe-ref]
|
539
544
|
!areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector)
|
@@ -222,13 +222,15 @@ function handleMissedUpdates(
|
|
222
222
|
}
|
223
223
|
}
|
224
224
|
|
225
|
+
type PromiseWithDisplayName = Promise<mixed> & {displayName?: string};
|
226
|
+
|
225
227
|
function handleMissingClientEdge(
|
226
228
|
environment: IEnvironment,
|
227
229
|
parentFragmentNode: ReaderFragment,
|
228
230
|
parentFragmentRef: mixed,
|
229
231
|
missingClientEdgeRequestInfo: MissingClientEdgeRequestInfo,
|
230
232
|
queryOptions?: FragmentQueryOptions,
|
231
|
-
): [QueryResult, ?
|
233
|
+
): [QueryResult, ?PromiseWithDisplayName] {
|
232
234
|
const originalVariables = getVariablesFromFragment(
|
233
235
|
parentFragmentNode,
|
234
236
|
parentFragmentRef,
|
@@ -253,10 +255,17 @@ function handleMissingClientEdge(
|
|
253
255
|
queryOptions?.fetchPolicy,
|
254
256
|
);
|
255
257
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
258
|
+
const promise = getPromiseForActiveRequest(
|
259
|
+
environment,
|
260
|
+
queryOperationDescriptor.request,
|
261
|
+
);
|
262
|
+
// $FlowExpectedError[prop-missing]
|
263
|
+
if (promise != null && promise.displayName == null) {
|
264
|
+
// $FlowExpectedError[prop-missing]
|
265
|
+
promise.displayName = missingClientEdgeRequestInfo.request.params.name;
|
266
|
+
}
|
267
|
+
// $FlowFixMe[incompatible-exact] - Intentionally bypassing exactness check
|
268
|
+
return [queryResult, promise];
|
260
269
|
}
|
261
270
|
|
262
271
|
function subscribeToSnapshot(
|
@@ -427,9 +436,9 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
427
436
|
'- Conditionally fetching `%s` but unconditionally passing %s prop ' +
|
428
437
|
'to `%s`. If the parent fragment only fetches the fragment conditionally ' +
|
429
438
|
'- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' +
|
430
|
-
'spread
|
431
|
-
'
|
432
|
-
'
|
439
|
+
'spread - then the fragment reference will not exist. ' +
|
440
|
+
'This issue can generally be fixed by adding `@alias` after `...%s`.\n' +
|
441
|
+
'See https://relay.dev/docs/next/guides/alias-directive/',
|
433
442
|
fragmentNode.name,
|
434
443
|
fragmentNode.name,
|
435
444
|
hookDisplayName,
|
@@ -476,7 +485,10 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
476
485
|
|
477
486
|
// Handle the queries for any missing client edges; this may suspend.
|
478
487
|
// FIXME handle client edges in parallel.
|
479
|
-
if (
|
488
|
+
if (
|
489
|
+
fragmentNode.metadata?.hasClientEdges === true ||
|
490
|
+
RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES
|
491
|
+
) {
|
480
492
|
// The fragment is validated to be static (in useFragment) and hasClientEdges is
|
481
493
|
// a static (constant) property of the fragment. In practice, this effect will
|
482
494
|
// always or never run for a given invocation of this hook.
|
@@ -487,7 +499,7 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
487
499
|
const missingClientEdges = getMissingClientEdges(state);
|
488
500
|
// eslint-disable-next-line no-shadow
|
489
501
|
let clientEdgeQueries;
|
490
|
-
const activeRequestPromises = [];
|
502
|
+
const activeRequestPromises: Array<PromiseWithDisplayName> = [];
|
491
503
|
if (missingClientEdges?.length) {
|
492
504
|
clientEdgeQueries = ([]: Array<QueryResult>);
|
493
505
|
for (const edge of missingClientEdges) {
|
@@ -508,7 +520,12 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
508
520
|
}, [state, environment, fragmentNode, fragmentRef, queryOptions]);
|
509
521
|
|
510
522
|
if (activeRequestPromises.length) {
|
511
|
-
|
523
|
+
const allPromises = Promise.all(activeRequestPromises);
|
524
|
+
// $FlowExpectedError[prop-missing] Expando to annotate Promises.
|
525
|
+
allPromises.displayName = `RelayClientEdge(${activeRequestPromises
|
526
|
+
.map(promise => promise.displayName)
|
527
|
+
.join(',')})`;
|
528
|
+
throw allPromises;
|
512
529
|
}
|
513
530
|
|
514
531
|
// See above note
|
@@ -535,12 +552,15 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
535
552
|
// Suspend if a Live Resolver within this fragment is in a suspended state:
|
536
553
|
const suspendingLiveResolvers = getSuspendingLiveResolver(state);
|
537
554
|
if (suspendingLiveResolvers != null && suspendingLiveResolvers.length > 0) {
|
538
|
-
|
555
|
+
const promise = Promise.all(
|
539
556
|
suspendingLiveResolvers.map(liveStateID => {
|
540
557
|
// $FlowFixMe[prop-missing] This is expected to be a RelayModernStore
|
541
558
|
return environment.getStore().getLiveResolverPromise(liveStateID);
|
542
559
|
}),
|
543
560
|
);
|
561
|
+
// $FlowExpectedError[prop-missing] Expando to annotate Promises.
|
562
|
+
promise.displayName = 'RelayLiveResolver(' + fragmentNode.name + ')';
|
563
|
+
throw promise;
|
544
564
|
}
|
545
565
|
// Suspend if an active operation bears on this fragment, either the
|
546
566
|
// fragment's owner or some other mutation etc. that could affect it.
|
@@ -549,6 +569,7 @@ hook useFragmentInternal_EXPERIMENTAL(
|
|
549
569
|
if (
|
550
570
|
RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE ||
|
551
571
|
environment !== previousEnvironment ||
|
572
|
+
// $FlowFixMe[sketchy-null-bool]
|
552
573
|
!committedFragmentSelectorRef.current ||
|
553
574
|
// $FlowFixMe[react-rule-unsafe-ref]
|
554
575
|
!areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector)
|
@@ -32,23 +32,72 @@ const {
|
|
32
32
|
export type UseLazyLoadQueryHookType = hook <TVariables: Variables, TData>(
|
33
33
|
gqlQuery: Query<TVariables, TData>,
|
34
34
|
variables: TVariables,
|
35
|
-
options?:
|
36
|
-
fetchKey?: string | number,
|
37
|
-
fetchPolicy?: FetchPolicy,
|
38
|
-
networkCacheConfig?: CacheConfig,
|
39
|
-
UNSTABLE_renderPolicy?: RenderPolicy,
|
40
|
-
}>,
|
35
|
+
options?: Options,
|
41
36
|
) => TData;
|
42
37
|
|
38
|
+
type Options = {
|
39
|
+
/**
|
40
|
+
* Determines if cached data should be used, and when to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides):
|
41
|
+
* * "store-or-network": _*(default)*_ *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made.
|
42
|
+
* * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not.
|
43
|
+
* * "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay.
|
44
|
+
* * "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../guided-tour/updating-data/local-data-updates).
|
45
|
+
*/
|
46
|
+
+fetchPolicy?: FetchPolicy,
|
47
|
+
/**
|
48
|
+
* A `fetchKey` can be passed to force a re-evaluation of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different `key` to a React component will cause it to remount). If the `fetchKey` is different from the one used in the previous render, the current query will be re-evaluated against the store, and it might be refetched depending on the current `fetchPolicy` and the state of the cache.
|
49
|
+
*/
|
50
|
+
+fetchKey?: string | number,
|
51
|
+
/**
|
52
|
+
* Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option.
|
53
|
+
*/
|
54
|
+
+networkCacheConfig?: CacheConfig,
|
55
|
+
/**
|
56
|
+
* Undocumented option.
|
57
|
+
*/
|
58
|
+
+UNSTABLE_renderPolicy?: RenderPolicy,
|
59
|
+
};
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Hook used to fetch a GraphQL query during render. This hook can trigger multiple nested or waterfalling round trips if used without caution, and waits until render to start a data fetch (when it can usually start a lot sooner than render), thereby degrading performance. Instead, prefer [`usePreloadedQuery`](../use-preloaded-query).
|
63
|
+
*
|
64
|
+
* @example
|
65
|
+
* const React = require('React');
|
66
|
+
*
|
67
|
+
* const {graphql, useLazyLoadQuery} = require('react-relay');
|
68
|
+
*
|
69
|
+
* function App() {
|
70
|
+
* const data = useLazyLoadQuery(
|
71
|
+
* graphql`
|
72
|
+
* query AppQuery($id: ID!) {
|
73
|
+
* user(id: $id) {
|
74
|
+
* name
|
75
|
+
* }
|
76
|
+
* }
|
77
|
+
* `,
|
78
|
+
* {id: 4},
|
79
|
+
* {fetchPolicy: 'store-or-network'},
|
80
|
+
* );
|
81
|
+
*
|
82
|
+
* return <h1>{data.user?.name}</h1>;
|
83
|
+
* }
|
84
|
+
*
|
85
|
+
* @returns - `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query.
|
86
|
+
* - The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{| user: ?{| name: ?string |} |}`.
|
87
|
+
*/
|
43
88
|
hook useLazyLoadQuery<TVariables: Variables, TData>(
|
89
|
+
/**
|
90
|
+
* GraphQL query specified using a `graphql` template literal.
|
91
|
+
*/
|
44
92
|
gqlQuery: Query<TVariables, TData>,
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
93
|
+
/**
|
94
|
+
* Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query.
|
95
|
+
*/
|
96
|
+
variables: NoInfer<TVariables>,
|
97
|
+
/**
|
98
|
+
* options object
|
99
|
+
*/
|
100
|
+
options?: Options,
|
52
101
|
): TData {
|
53
102
|
const environment = useRelayEnvironment();
|
54
103
|
|
@@ -71,4 +120,4 @@ hook useLazyLoadQuery<TVariables: Variables, TData>(
|
|
71
120
|
}
|
72
121
|
|
73
122
|
// $FlowFixMe[react-rule-hook-incompatible]
|
74
|
-
module.exports =
|
123
|
+
module.exports = useLazyLoadQuery;
|
@@ -78,7 +78,7 @@ hook useLazyLoadQueryNode<TQuery: OperationType>({
|
|
78
78
|
useEffect(() => {
|
79
79
|
return () => {
|
80
80
|
// Attempt to detect if the component was
|
81
|
-
// hidden (by Offscreen API), or fast refresh
|
81
|
+
// hidden (by Offscreen API), or fast refresh occurred;
|
82
82
|
// Only in these situations would the effect cleanup
|
83
83
|
// for "unmounting" run multiple times, so if
|
84
84
|
// we are ever able to read this ref with a value
|
@@ -16,7 +16,7 @@ import type {
|
|
16
16
|
PreloadedQueryInner,
|
17
17
|
PreloadedQueryInner_DEPRECATED,
|
18
18
|
} from './EntryPointTypes.flow';
|
19
|
-
import type {Query, RenderPolicy, Variables} from 'relay-runtime';
|
19
|
+
import type {ClientQuery, Query, RenderPolicy, Variables} from 'relay-runtime';
|
20
20
|
|
21
21
|
const useLazyLoadQueryNode = require('./useLazyLoadQueryNode');
|
22
22
|
const useMemoOperationDescriptor = require('./useMemoOperationDescriptor');
|
@@ -56,7 +56,9 @@ hook usePreloadedQuery<
|
|
56
56
|
TData,
|
57
57
|
TRawResponse: ?{...} = void,
|
58
58
|
>(
|
59
|
-
gqlQuery:
|
59
|
+
gqlQuery:
|
60
|
+
| Query<TVariables, TData, TRawResponse>
|
61
|
+
| ClientQuery<TVariables, TData, TRawResponse>,
|
60
62
|
preloadedQuery: PreloadedQuery<
|
61
63
|
TVariables,
|
62
64
|
TData,
|
@@ -126,7 +128,7 @@ hook usePreloadedQuery<
|
|
126
128
|
// context, we cannot re-use the existing preloaded query.
|
127
129
|
// Instead, we need to fall back and re-execute and de-dupe the query with
|
128
130
|
// the new environment (at render time).
|
129
|
-
// TODO T68036756 track
|
131
|
+
// TODO T68036756 track occurrences of this warning and turn it into a hard error
|
130
132
|
warning(
|
131
133
|
false,
|
132
134
|
'usePreloadedQuery(): usePreloadedQuery was passed a preloaded query ' +
|
@@ -155,6 +157,8 @@ hook usePreloadedQuery<
|
|
155
157
|
variables: TVariables,
|
156
158
|
response: TData,
|
157
159
|
rawResponse?: $NonMaybeType<TRawResponse>,
|
160
|
+
/* $FlowFixMe[incompatible-call] Natural Inference rollout. See
|
161
|
+
* https://fburl.com/gdoc/y8dn025u */
|
158
162
|
}>(useLazyLoadQueryNodeParams);
|
159
163
|
|
160
164
|
if (__DEV__) {
|
@@ -236,7 +236,7 @@ hook useQueryLoader_CURRENT<
|
|
236
236
|
useEffect(() => {
|
237
237
|
return () => {
|
238
238
|
// Attempt to detect if the component was
|
239
|
-
// hidden (by Offscreen API), or fast refresh
|
239
|
+
// hidden (by Offscreen API), or fast refresh occurred;
|
240
240
|
// Only in these situations would the effect cleanup
|
241
241
|
// for "unmounting" run multiple times, so if
|
242
242
|
// we are ever able to read this ref with a value
|
@@ -17,6 +17,28 @@ const ReactRelayContext = require('./../ReactRelayContext');
|
|
17
17
|
const invariant = require('invariant');
|
18
18
|
const {useContext} = require('react');
|
19
19
|
|
20
|
+
/**
|
21
|
+
* Hook used to access a Relay environment that was set by a [`RelayEnvironmentProvider`](../relay-environment-provider):
|
22
|
+
*
|
23
|
+
* @example
|
24
|
+
* const React = require('React');
|
25
|
+
*
|
26
|
+
* const {useRelayEnvironment} = require('react-relay');
|
27
|
+
*
|
28
|
+
* function MyComponent() {
|
29
|
+
* const environment = useRelayEnvironment();
|
30
|
+
*
|
31
|
+
* const handler = useCallback(() => {
|
32
|
+
* // For example, can be used to pass the environment to functions
|
33
|
+
* // that require a Relay environment.
|
34
|
+
* commitMutation(environment, ...);
|
35
|
+
* }, [environment])
|
36
|
+
*
|
37
|
+
* return (...);
|
38
|
+
* }
|
39
|
+
*
|
40
|
+
* module.exports = MyComponent;
|
41
|
+
*/
|
20
42
|
hook useRelayEnvironment(): IEnvironment {
|
21
43
|
const context = useContext(ReactRelayContext);
|
22
44
|
invariant(
|