react-relay 12.0.0 → 13.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/LICENSE +1 -1
- package/README.md +47 -0
- package/ReactRelayContainerUtils.js.flow +1 -1
- package/ReactRelayContext.js +2 -2
- package/ReactRelayContext.js.flow +3 -4
- package/ReactRelayFragmentContainer.js.flow +11 -17
- package/ReactRelayFragmentMockRenderer.js.flow +2 -2
- package/ReactRelayLocalQueryRenderer.js.flow +7 -8
- package/ReactRelayPaginationContainer.js.flow +30 -40
- package/ReactRelayQueryFetcher.js.flow +10 -11
- package/ReactRelayQueryRenderer.js.flow +16 -16
- package/ReactRelayQueryRendererContext.js.flow +1 -1
- package/ReactRelayRefetchContainer.js.flow +25 -33
- package/ReactRelayTestMocker.js.flow +17 -15
- package/ReactRelayTypes.js.flow +11 -11
- package/RelayContext.js.flow +4 -4
- package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +2 -3
- package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +2 -3
- package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +2 -3
- package/__flowtests__/RelayModern-flowtest.js.flow +79 -47
- package/__flowtests__/RelayModernFlowtest_badref.graphql.js.flow +6 -5
- package/__flowtests__/RelayModernFlowtest_notref.graphql.js.flow +6 -5
- package/__flowtests__/RelayModernFlowtest_user.graphql.js.flow +5 -4
- package/__flowtests__/RelayModernFlowtest_users.graphql.js.flow +5 -4
- package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +14 -11
- package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +14 -11
- package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +14 -9
- package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +14 -11
- package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +14 -9
- package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +14 -11
- package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +16 -13
- package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +16 -13
- package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +14 -11
- package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +14 -11
- package/assertFragmentMap.js.flow +3 -3
- package/buildReactRelayContainer.js.flow +12 -11
- package/getRootVariablesForFragments.js.flow +3 -5
- package/hooks.js +2 -2
- package/hooks.js.flow +4 -6
- package/index.js +2 -2
- package/index.js.flow +5 -7
- package/isRelayEnvironment.js.flow +1 -1
- package/jest-react/enqueueTask.js.flow +2 -2
- package/jest-react/index.js.flow +1 -1
- package/jest-react/internalAct.js.flow +2 -4
- package/legacy.js +2 -2
- package/legacy.js.flow +1 -1
- package/lib/ReactRelayContainerUtils.js +1 -1
- package/lib/ReactRelayContext.js +1 -1
- package/lib/ReactRelayFragmentContainer.js +5 -5
- package/lib/ReactRelayFragmentMockRenderer.js +3 -3
- package/lib/ReactRelayLocalQueryRenderer.js +8 -9
- package/lib/ReactRelayPaginationContainer.js +19 -23
- package/lib/ReactRelayQueryFetcher.js +3 -3
- package/lib/ReactRelayQueryRenderer.js +5 -5
- package/lib/ReactRelayQueryRendererContext.js +1 -1
- package/lib/ReactRelayRefetchContainer.js +13 -15
- package/lib/ReactRelayTestMocker.js +8 -9
- package/lib/ReactRelayTypes.js +1 -1
- package/lib/RelayContext.js +4 -3
- package/lib/assertFragmentMap.js +3 -2
- package/lib/buildReactRelayContainer.js +8 -8
- package/lib/getRootVariablesForFragments.js +2 -3
- package/lib/hooks.js +6 -6
- package/lib/index.js +8 -8
- package/lib/isRelayEnvironment.js +1 -1
- package/lib/jest-react/enqueueTask.js +1 -1
- package/lib/jest-react/internalAct.js +3 -4
- package/lib/legacy.js +1 -1
- package/lib/multi-actor/ActorChange.js +3 -3
- package/lib/multi-actor/index.js +1 -1
- package/lib/multi-actor/useRelayActorEnvironment.js +3 -3
- package/lib/readContext.js +1 -1
- package/lib/relay-hooks/EntryPointContainer.react.js +4 -4
- package/lib/relay-hooks/EntryPointTypes.flow.js +1 -1
- package/lib/relay-hooks/FragmentResource.js +242 -46
- package/lib/relay-hooks/InternalLogger.js +1 -1
- package/lib/relay-hooks/LRUCache.js +1 -1
- package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +5 -5
- package/lib/relay-hooks/MatchContainer.js +2 -2
- package/lib/relay-hooks/ProfilerContext.js +1 -1
- package/lib/relay-hooks/QueryResource.js +84 -5
- package/lib/relay-hooks/RelayEnvironmentProvider.js +1 -1
- package/lib/relay-hooks/SuspenseResource.js +130 -0
- package/lib/relay-hooks/loadEntryPoint.js +1 -1
- package/lib/relay-hooks/loadQuery.js +9 -10
- package/lib/relay-hooks/preloadQuery_DEPRECATED.js +25 -11
- package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +1 -1
- package/lib/relay-hooks/useBlockingPaginationFragment.js +3 -3
- package/lib/relay-hooks/useEntryPointLoader.js +3 -3
- package/lib/relay-hooks/useFetchTrackingRef.js +3 -2
- package/lib/relay-hooks/useFragment.js +7 -7
- package/lib/relay-hooks/useFragmentNode.js +5 -5
- package/lib/relay-hooks/useIsMountedRef.js +1 -1
- package/lib/relay-hooks/useIsOperationNodeActive.js +3 -3
- package/lib/relay-hooks/useIsParentQueryActive.js +1 -1
- package/lib/relay-hooks/useLazyLoadQuery.js +4 -4
- package/lib/relay-hooks/useLazyLoadQueryNode.js +5 -5
- package/lib/relay-hooks/useLoadMoreFunction.js +8 -10
- package/lib/relay-hooks/useMemoOperationDescriptor.js +3 -3
- package/lib/relay-hooks/useMemoVariables.js +3 -3
- package/lib/relay-hooks/useMutation.js +18 -7
- package/lib/relay-hooks/usePaginationFragment.js +1 -1
- package/lib/relay-hooks/usePreloadedQuery.js +6 -6
- package/lib/relay-hooks/useQueryLoader.js +5 -5
- package/lib/relay-hooks/useRefetchableFragment.js +1 -1
- package/lib/relay-hooks/useRefetchableFragmentNode.js +11 -13
- package/lib/relay-hooks/useRelayEnvironment.js +3 -3
- package/lib/relay-hooks/useStaticFragmentNodeWarning.js +3 -3
- package/lib/relay-hooks/useSubscribeToInvalidationState.js +3 -2
- package/lib/relay-hooks/useSubscription.js +1 -1
- package/multi-actor/ActorChange.js.flow +4 -5
- package/multi-actor/index.js.flow +1 -1
- package/multi-actor/useRelayActorEnvironment.js.flow +6 -8
- package/package.json +3 -3
- package/react-relay-hooks.js +2 -2
- package/react-relay-hooks.min.js +3 -3
- package/react-relay-legacy.js +2 -2
- package/react-relay-legacy.min.js +3 -3
- package/react-relay.js +2 -2
- package/react-relay.min.js +3 -3
- package/readContext.js.flow +1 -1
- package/relay-hooks/EntryPointContainer.react.js.flow +9 -16
- package/relay-hooks/EntryPointTypes.flow.js.flow +19 -25
- package/relay-hooks/FragmentResource.js.flow +221 -35
- package/relay-hooks/InternalLogger.js.flow +1 -1
- package/relay-hooks/LRUCache.js.flow +1 -1
- package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +33 -47
- package/relay-hooks/MatchContainer.js.flow +4 -3
- package/relay-hooks/ProfilerContext.js.flow +1 -1
- package/relay-hooks/QueryResource.js.flow +120 -18
- package/relay-hooks/RelayEnvironmentProvider.js.flow +10 -10
- package/relay-hooks/SuspenseResource.js.flow +115 -0
- package/relay-hooks/__flowtests__/EntryPointTypes/EntryPointElementConfig-flowtest.js.flow +5 -4
- package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +2 -2
- package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +59 -0
- package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +61 -0
- package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +11 -10
- package/relay-hooks/__flowtests__/useFragment-flowtest.js.flow +55 -32
- package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +11 -10
- package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +11 -10
- package/relay-hooks/__flowtests__/utils.js.flow +21 -32
- package/relay-hooks/loadEntryPoint.js.flow +7 -13
- package/relay-hooks/loadQuery.js.flow +23 -24
- package/relay-hooks/preloadQuery_DEPRECATED.js.flow +30 -14
- package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +7 -13
- package/relay-hooks/useBlockingPaginationFragment.js.flow +13 -14
- package/relay-hooks/useEntryPointLoader.js.flow +8 -11
- package/relay-hooks/useFetchTrackingRef.js.flow +3 -3
- package/relay-hooks/useFragment.js.flow +31 -62
- package/relay-hooks/useFragmentNode.js.flow +6 -8
- package/relay-hooks/useIsMountedRef.js.flow +1 -1
- package/relay-hooks/useIsOperationNodeActive.js.flow +4 -6
- package/relay-hooks/useIsParentQueryActive.js.flow +4 -5
- package/relay-hooks/useLazyLoadQuery.js.flow +14 -16
- package/relay-hooks/useLazyLoadQueryNode.js.flow +12 -14
- package/relay-hooks/useLoadMoreFunction.js.flow +20 -28
- package/relay-hooks/useMemoOperationDescriptor.js.flow +6 -8
- package/relay-hooks/useMemoVariables.js.flow +7 -7
- package/relay-hooks/useMutation.js.flow +27 -27
- package/relay-hooks/usePaginationFragment.js.flow +38 -47
- package/relay-hooks/usePreloadedQuery.js.flow +14 -20
- package/relay-hooks/useQueryLoader.js.flow +14 -17
- package/relay-hooks/useRefetchableFragment.js.flow +8 -9
- package/relay-hooks/useRefetchableFragmentNode.js.flow +23 -31
- package/relay-hooks/useRelayEnvironment.js.flow +3 -5
- package/relay-hooks/useStaticFragmentNodeWarning.js.flow +3 -4
- package/relay-hooks/useSubscribeToInvalidationState.js.flow +4 -7
- package/relay-hooks/useSubscription.js.flow +7 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -13,28 +13,35 @@
|
|
|
13
13
|
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
import type {Cache} from './LRUCache';
|
|
17
|
+
import type {QueryResource, QueryResult} from './QueryResource';
|
|
18
|
+
import type {
|
|
19
|
+
ConcreteRequest,
|
|
20
|
+
DataID,
|
|
21
|
+
Disposable,
|
|
22
|
+
IEnvironment,
|
|
23
|
+
ReaderFragment,
|
|
24
|
+
RequestDescriptor,
|
|
25
|
+
Snapshot,
|
|
26
|
+
} from 'relay-runtime';
|
|
17
27
|
|
|
28
|
+
const LRUCache = require('./LRUCache');
|
|
29
|
+
const {getQueryResourceForEnvironment} = require('./QueryResource');
|
|
30
|
+
const SuspenseResource = require('./SuspenseResource');
|
|
18
31
|
const invariant = require('invariant');
|
|
19
|
-
|
|
20
32
|
const {
|
|
33
|
+
RelayFeatureFlags,
|
|
34
|
+
__internal: {fetchQuery, getPromiseForActiveRequest},
|
|
35
|
+
createOperationDescriptor,
|
|
21
36
|
getFragmentIdentifier,
|
|
22
37
|
getPendingOperationsForFragment,
|
|
23
38
|
getSelector,
|
|
39
|
+
getVariablesFromFragment,
|
|
24
40
|
isPromise,
|
|
25
41
|
recycleNodesInto,
|
|
26
42
|
reportMissingRequiredFields,
|
|
27
43
|
} = require('relay-runtime');
|
|
28
44
|
|
|
29
|
-
import type {Cache} from './LRUCache';
|
|
30
|
-
import type {
|
|
31
|
-
Disposable,
|
|
32
|
-
IEnvironment,
|
|
33
|
-
ReaderFragment,
|
|
34
|
-
RequestDescriptor,
|
|
35
|
-
Snapshot,
|
|
36
|
-
} from 'relay-runtime';
|
|
37
|
-
|
|
38
45
|
export type FragmentResource = FragmentResourceImpl;
|
|
39
46
|
|
|
40
47
|
type FragmentResourceCache = Cache<
|
|
@@ -78,6 +85,24 @@ function isMissingData(snapshot: SingularOrPluralSnapshot): boolean {
|
|
|
78
85
|
return snapshot.isMissingData;
|
|
79
86
|
}
|
|
80
87
|
|
|
88
|
+
function hasMissingClientEdges(snapshot: SingularOrPluralSnapshot): boolean {
|
|
89
|
+
if (Array.isArray(snapshot)) {
|
|
90
|
+
return snapshot.some(s => (s.missingClientEdges?.length ?? 0) > 0);
|
|
91
|
+
}
|
|
92
|
+
return (snapshot.missingClientEdges?.length ?? 0) > 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function singularOrPluralForEach(
|
|
96
|
+
snapshot: SingularOrPluralSnapshot,
|
|
97
|
+
f: Snapshot => void,
|
|
98
|
+
): void {
|
|
99
|
+
if (Array.isArray(snapshot)) {
|
|
100
|
+
snapshot.forEach(f);
|
|
101
|
+
} else {
|
|
102
|
+
f(snapshot);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
81
106
|
function getFragmentResult(
|
|
82
107
|
cacheKey: string,
|
|
83
108
|
snapshot: SingularOrPluralSnapshot,
|
|
@@ -101,13 +126,75 @@ function getFragmentResult(
|
|
|
101
126
|
};
|
|
102
127
|
}
|
|
103
128
|
|
|
129
|
+
/**
|
|
130
|
+
* The purpose of this cache is to allow information to be passed from an
|
|
131
|
+
* initial read which suspends through to the commit that follows a subsequent
|
|
132
|
+
* successful read. Specifically, the QueryResource result for the data fetch
|
|
133
|
+
* is passed through so that that query can be retained on commit.
|
|
134
|
+
*/
|
|
135
|
+
class ClientEdgeQueryResultsCache {
|
|
136
|
+
_cache: Map<string, [Array<QueryResult>, SuspenseResource]> = new Map();
|
|
137
|
+
_retainCounts: Map<string, number> = new Map();
|
|
138
|
+
_environment: IEnvironment;
|
|
139
|
+
|
|
140
|
+
constructor(environment: IEnvironment) {
|
|
141
|
+
this._environment = environment;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
get(fragmentIdentifier: string): void | Array<QueryResult> {
|
|
145
|
+
return this._cache.get(fragmentIdentifier)?.[0] ?? undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
recordQueryResults(
|
|
149
|
+
fragmentIdentifier: string,
|
|
150
|
+
value: Array<QueryResult>, // may be mutated after being passed here
|
|
151
|
+
): void {
|
|
152
|
+
const existing = this._cache.get(fragmentIdentifier);
|
|
153
|
+
if (!existing) {
|
|
154
|
+
const suspenseResource = new SuspenseResource(() =>
|
|
155
|
+
this._retain(fragmentIdentifier),
|
|
156
|
+
);
|
|
157
|
+
this._cache.set(fragmentIdentifier, [value, suspenseResource]);
|
|
158
|
+
suspenseResource.temporaryRetain(this._environment);
|
|
159
|
+
} else {
|
|
160
|
+
const [existingResults, suspenseResource] = existing;
|
|
161
|
+
value.forEach(queryResult => {
|
|
162
|
+
existingResults.push(queryResult);
|
|
163
|
+
});
|
|
164
|
+
suspenseResource.temporaryRetain(this._environment);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
_retain(id) {
|
|
169
|
+
const retainCount = (this._retainCounts.get(id) ?? 0) + 1;
|
|
170
|
+
this._retainCounts.set(id, retainCount);
|
|
171
|
+
return {
|
|
172
|
+
dispose: () => {
|
|
173
|
+
const newRetainCount = (this._retainCounts.get(id) ?? 0) - 1;
|
|
174
|
+
if (newRetainCount > 0) {
|
|
175
|
+
this._retainCounts.set(id, newRetainCount);
|
|
176
|
+
} else {
|
|
177
|
+
this._retainCounts.delete(id);
|
|
178
|
+
this._cache.delete(id);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
104
185
|
class FragmentResourceImpl {
|
|
105
186
|
_environment: IEnvironment;
|
|
106
187
|
_cache: FragmentResourceCache;
|
|
188
|
+
_clientEdgeQueryResultsCache: void | ClientEdgeQueryResultsCache;
|
|
107
189
|
|
|
108
190
|
constructor(environment: IEnvironment) {
|
|
109
191
|
this._environment = environment;
|
|
110
192
|
this._cache = LRUCache.create(CACHE_CAPACITY);
|
|
193
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
194
|
+
this._clientEdgeQueryResultsCache = new ClientEdgeQueryResultsCache(
|
|
195
|
+
environment,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
111
198
|
}
|
|
112
199
|
|
|
113
200
|
/**
|
|
@@ -252,24 +339,75 @@ class FragmentResourceImpl {
|
|
|
252
339
|
return fragmentResult;
|
|
253
340
|
}
|
|
254
341
|
|
|
255
|
-
// 3. If we don't have data in the store,
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
342
|
+
// 3. If we don't have data in the store, there's two cases where we should
|
|
343
|
+
// suspend to await the data: First if any client edges were traversed where
|
|
344
|
+
// the destination record was missing data; in that case we initiate a query
|
|
345
|
+
// here to fetch the missing data. Second, there may already be a request
|
|
346
|
+
// in flight for the fragment's parent query, or for another operation that
|
|
347
|
+
// may affect the parent's query data, such as a mutation or subscription.
|
|
348
|
+
// For any of these cases we can get a promise, which we will cache and
|
|
349
|
+
// suspend on.
|
|
350
|
+
|
|
351
|
+
// First, initiate a query for any client edges that were missing data:
|
|
352
|
+
let clientEdgeRequests: ?Array<RequestDescriptor> = null;
|
|
353
|
+
if (
|
|
354
|
+
RelayFeatureFlags.ENABLE_CLIENT_EDGES &&
|
|
355
|
+
hasMissingClientEdges(snapshot)
|
|
356
|
+
) {
|
|
357
|
+
clientEdgeRequests = [];
|
|
358
|
+
const queryResource = getQueryResourceForEnvironment(this._environment);
|
|
359
|
+
const queryResults = [];
|
|
360
|
+
singularOrPluralForEach(snapshot, snap => {
|
|
361
|
+
snap.missingClientEdges?.forEach(
|
|
362
|
+
({request, clientEdgeDestinationID}) => {
|
|
363
|
+
const {queryResult, requestDescriptor} =
|
|
364
|
+
this._performClientEdgeQuery(
|
|
365
|
+
queryResource,
|
|
366
|
+
fragmentNode,
|
|
367
|
+
fragmentRef,
|
|
368
|
+
request,
|
|
369
|
+
clientEdgeDestinationID,
|
|
370
|
+
);
|
|
371
|
+
queryResults.push(queryResult);
|
|
372
|
+
clientEdgeRequests?.push(requestDescriptor);
|
|
373
|
+
},
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
// Store the query so that it can be retained when our own fragment is
|
|
377
|
+
// subscribed to. This merges with any existing query results:
|
|
378
|
+
invariant(
|
|
379
|
+
this._clientEdgeQueryResultsCache != null,
|
|
380
|
+
'Client edge query result cache should exist when ENABLE_CLIENT_EDGES is on.',
|
|
381
|
+
);
|
|
382
|
+
this._clientEdgeQueryResultsCache.recordQueryResults(
|
|
383
|
+
fragmentIdentifier,
|
|
384
|
+
queryResults,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
let clientEdgePromises = null;
|
|
388
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES && clientEdgeRequests) {
|
|
389
|
+
clientEdgePromises = clientEdgeRequests
|
|
390
|
+
.map(request => getPromiseForActiveRequest(this._environment, request))
|
|
391
|
+
.filter(p => p != null);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Finally look for operations in flight for our parent query:
|
|
260
395
|
const fragmentOwner =
|
|
261
396
|
fragmentSelector.kind === 'PluralReaderSelector'
|
|
262
397
|
? fragmentSelector.selectors[0].owner
|
|
263
398
|
: fragmentSelector.owner;
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
399
|
+
const parentQueryPromiseResult =
|
|
400
|
+
this._getAndSavePromiseForFragmentRequestInFlight(
|
|
401
|
+
fragmentIdentifier,
|
|
402
|
+
fragmentNode,
|
|
403
|
+
fragmentOwner,
|
|
404
|
+
fragmentResult,
|
|
405
|
+
);
|
|
406
|
+
const parentQueryPromiseResultPromise = parentQueryPromiseResult?.promise; // for refinement
|
|
407
|
+
|
|
270
408
|
if (
|
|
271
|
-
|
|
272
|
-
isPromise(
|
|
409
|
+
clientEdgePromises?.length ||
|
|
410
|
+
isPromise(parentQueryPromiseResultPromise)
|
|
273
411
|
) {
|
|
274
412
|
environment.__log({
|
|
275
413
|
name: 'suspense.fragment',
|
|
@@ -278,15 +416,52 @@ class FragmentResourceImpl {
|
|
|
278
416
|
isRelayHooks: true,
|
|
279
417
|
isPromiseCached: false,
|
|
280
418
|
isMissingData: fragmentResult.isMissingData,
|
|
281
|
-
pendingOperations:
|
|
419
|
+
pendingOperations: [
|
|
420
|
+
...(parentQueryPromiseResult?.pendingOperations ?? []),
|
|
421
|
+
...(clientEdgeRequests ?? []),
|
|
422
|
+
],
|
|
282
423
|
});
|
|
283
|
-
throw
|
|
424
|
+
throw clientEdgePromises?.length
|
|
425
|
+
? Promise.all([parentQueryPromiseResultPromise, ...clientEdgePromises])
|
|
426
|
+
: parentQueryPromiseResultPromise;
|
|
284
427
|
}
|
|
285
428
|
|
|
286
429
|
this._reportMissingRequiredFieldsInSnapshot(snapshot);
|
|
287
430
|
return getFragmentResult(fragmentIdentifier, snapshot, storeEpoch);
|
|
288
431
|
}
|
|
289
432
|
|
|
433
|
+
_performClientEdgeQuery(
|
|
434
|
+
queryResource: QueryResource,
|
|
435
|
+
fragmentNode: ReaderFragment,
|
|
436
|
+
fragmentRef: mixed,
|
|
437
|
+
request: ConcreteRequest,
|
|
438
|
+
clientEdgeDestinationID: DataID,
|
|
439
|
+
) {
|
|
440
|
+
const originalVariables = getVariablesFromFragment(
|
|
441
|
+
fragmentNode,
|
|
442
|
+
fragmentRef,
|
|
443
|
+
);
|
|
444
|
+
const variables = {
|
|
445
|
+
...originalVariables,
|
|
446
|
+
id: clientEdgeDestinationID, // TODO should be a reserved name
|
|
447
|
+
};
|
|
448
|
+
const operation = createOperationDescriptor(
|
|
449
|
+
request,
|
|
450
|
+
variables,
|
|
451
|
+
{}, // TODO cacheConfig should probably inherent from parent operation
|
|
452
|
+
);
|
|
453
|
+
const fetchObservable = fetchQuery(this._environment, operation);
|
|
454
|
+
const queryResult = queryResource.prepare(
|
|
455
|
+
operation,
|
|
456
|
+
fetchObservable,
|
|
457
|
+
// TODO should inherent render policy etc. from parent operation
|
|
458
|
+
);
|
|
459
|
+
return {
|
|
460
|
+
requestDescriptor: operation.request,
|
|
461
|
+
queryResult,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
290
465
|
_reportMissingRequiredFieldsInSnapshot(snapshot: SingularOrPluralSnapshot) {
|
|
291
466
|
if (Array.isArray(snapshot)) {
|
|
292
467
|
snapshot.forEach(s => {
|
|
@@ -334,9 +509,8 @@ class FragmentResourceImpl {
|
|
|
334
509
|
|
|
335
510
|
// 1. Check for any updates missed during render phase
|
|
336
511
|
// TODO(T44066760): More efficiently detect if we missed an update
|
|
337
|
-
const [didMissUpdates, currentSnapshot] =
|
|
338
|
-
fragmentResult
|
|
339
|
-
);
|
|
512
|
+
const [didMissUpdates, currentSnapshot] =
|
|
513
|
+
this.checkMissedUpdates(fragmentResult);
|
|
340
514
|
|
|
341
515
|
// 2. If an update was missed, notify the component so it updates with
|
|
342
516
|
// the latest data.
|
|
@@ -345,7 +519,7 @@ class FragmentResourceImpl {
|
|
|
345
519
|
}
|
|
346
520
|
|
|
347
521
|
// 3. Establish subscriptions on the snapshot(s)
|
|
348
|
-
const
|
|
522
|
+
const disposables = [];
|
|
349
523
|
if (Array.isArray(renderedSnapshot)) {
|
|
350
524
|
invariant(
|
|
351
525
|
Array.isArray(currentSnapshot),
|
|
@@ -353,7 +527,7 @@ class FragmentResourceImpl {
|
|
|
353
527
|
"If you're seeing this, this is likely a bug in Relay.",
|
|
354
528
|
);
|
|
355
529
|
currentSnapshot.forEach((snapshot, idx) => {
|
|
356
|
-
|
|
530
|
+
disposables.push(
|
|
357
531
|
environment.subscribe(snapshot, latestSnapshot => {
|
|
358
532
|
const storeEpoch = environment.getStore().getEpoch();
|
|
359
533
|
this._updatePluralSnapshot(
|
|
@@ -373,7 +547,7 @@ class FragmentResourceImpl {
|
|
|
373
547
|
'Relay: Expected snapshot to be singular. ' +
|
|
374
548
|
"If you're seeing this, this is likely a bug in Relay.",
|
|
375
549
|
);
|
|
376
|
-
|
|
550
|
+
disposables.push(
|
|
377
551
|
environment.subscribe(currentSnapshot, latestSnapshot => {
|
|
378
552
|
const storeEpoch = environment.getStore().getEpoch();
|
|
379
553
|
this._cache.set(cacheKey, {
|
|
@@ -385,9 +559,20 @@ class FragmentResourceImpl {
|
|
|
385
559
|
);
|
|
386
560
|
}
|
|
387
561
|
|
|
562
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
563
|
+
const clientEdgeQueryResults =
|
|
564
|
+
this._clientEdgeQueryResultsCache?.get(cacheKey) ?? undefined;
|
|
565
|
+
if (clientEdgeQueryResults?.length) {
|
|
566
|
+
const queryResource = getQueryResourceForEnvironment(this._environment);
|
|
567
|
+
clientEdgeQueryResults.forEach(queryResult => {
|
|
568
|
+
disposables.push(queryResource.retain(queryResult));
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
388
573
|
return {
|
|
389
574
|
dispose: () => {
|
|
390
|
-
|
|
575
|
+
disposables.forEach(s => s.dispose());
|
|
391
576
|
this._cache.delete(cacheKey);
|
|
392
577
|
},
|
|
393
578
|
};
|
|
@@ -458,6 +643,7 @@ class FragmentResourceImpl {
|
|
|
458
643
|
const updatedCurrentSnapshot: Snapshot = {
|
|
459
644
|
data: updatedData,
|
|
460
645
|
isMissingData: currentSnapshot.isMissingData,
|
|
646
|
+
missingClientEdges: currentSnapshot.missingClientEdges,
|
|
461
647
|
seenRecords: currentSnapshot.seenRecords,
|
|
462
648
|
selector: currentSnapshot.selector,
|
|
463
649
|
missingRequiredFields: currentSnapshot.missingRequiredFields,
|
|
@@ -473,7 +659,7 @@ class FragmentResourceImpl {
|
|
|
473
659
|
|
|
474
660
|
checkMissedUpdatesSpec(fragmentResults: {
|
|
475
661
|
[string]: FragmentResult,
|
|
476
|
-
|
|
662
|
+
...
|
|
477
663
|
}): boolean {
|
|
478
664
|
return Object.keys(fragmentResults).some(
|
|
479
665
|
key => this.checkMissedUpdates(fragmentResults[key])[0],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -13,36 +13,28 @@
|
|
|
13
13
|
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
import type {
|
|
17
|
+
EntryPoint,
|
|
18
|
+
EntryPointComponent,
|
|
19
|
+
EnvironmentProviderOptions,
|
|
20
|
+
IEnvironmentProvider,
|
|
21
|
+
} from './EntryPointTypes.flow';
|
|
18
22
|
|
|
19
23
|
const preloadQuery_DEPRECATED = require('./preloadQuery_DEPRECATED');
|
|
24
|
+
const ProfilerContext = require('./ProfilerContext');
|
|
20
25
|
const useRelayEnvironment = require('./useRelayEnvironment');
|
|
21
|
-
|
|
26
|
+
const React = require('react');
|
|
22
27
|
const {useContext, useEffect, useMemo} = require('react');
|
|
23
28
|
const {stableCopy} = require('relay-runtime');
|
|
24
29
|
|
|
25
30
|
type PreloadedEntryPoint<TEntryPointComponent> = $ReadOnly<{|
|
|
26
|
-
entryPoints:
|
|
27
|
-
|
|
28
|
-
'entryPoints',
|
|
29
|
-
>,
|
|
30
|
-
extraProps: $PropertyType<
|
|
31
|
-
React.ElementConfig<TEntryPointComponent>,
|
|
32
|
-
'extraProps',
|
|
33
|
-
>,
|
|
31
|
+
entryPoints: React.ElementConfig<TEntryPointComponent>['entryPoints'],
|
|
32
|
+
extraProps: React.ElementConfig<TEntryPointComponent>['extraProps'],
|
|
34
33
|
getComponent: () => TEntryPointComponent,
|
|
35
|
-
queries:
|
|
34
|
+
queries: React.ElementConfig<TEntryPointComponent>['queries'],
|
|
36
35
|
rootModuleID: string,
|
|
37
36
|
|}>;
|
|
38
37
|
|
|
39
|
-
import type {
|
|
40
|
-
EntryPoint,
|
|
41
|
-
EntryPointComponent,
|
|
42
|
-
EnvironmentProviderOptions,
|
|
43
|
-
IEnvironmentProvider,
|
|
44
|
-
} from './EntryPointTypes.flow';
|
|
45
|
-
|
|
46
38
|
type EntryPointContainerProps<
|
|
47
39
|
TEntryPointParams,
|
|
48
40
|
TPreloadedQueries,
|
|
@@ -100,12 +92,8 @@ function prepareEntryPoint<
|
|
|
100
92
|
if (queries != null) {
|
|
101
93
|
const queriesPropNames = Object.keys(queries);
|
|
102
94
|
queriesPropNames.forEach(queryPropName => {
|
|
103
|
-
const {
|
|
104
|
-
|
|
105
|
-
options,
|
|
106
|
-
parameters,
|
|
107
|
-
variables,
|
|
108
|
-
} = queries[queryPropName];
|
|
95
|
+
const {environmentProviderOptions, options, parameters, variables} =
|
|
96
|
+
queries[queryPropName];
|
|
109
97
|
|
|
110
98
|
const environment = environmentProvider.getEnvironment(
|
|
111
99
|
environmentProviderOptions,
|
|
@@ -128,10 +116,8 @@ function prepareEntryPoint<
|
|
|
128
116
|
if (entryPointDescription == null) {
|
|
129
117
|
return;
|
|
130
118
|
}
|
|
131
|
-
const {
|
|
132
|
-
|
|
133
|
-
entryPointParams: nestedParams,
|
|
134
|
-
} = entryPointDescription;
|
|
119
|
+
const {entryPoint: nestedEntryPoint, entryPointParams: nestedParams} =
|
|
120
|
+
entryPointDescription;
|
|
135
121
|
preloadedEntryPoints[entryPointPropName] = prepareEntryPoint(
|
|
136
122
|
environmentProvider,
|
|
137
123
|
nestedEntryPoint,
|
|
@@ -180,23 +166,23 @@ function LazyLoadEntryPointContainer_DEPRECATED<
|
|
|
180
166
|
// *must* be computed first to fetch the component's data-dependencies in
|
|
181
167
|
// parallel with the component itself (the code).
|
|
182
168
|
const entryPointParamsHash = stableStringify(entryPointParams);
|
|
183
|
-
const {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
169
|
+
const {getComponent, queries, entryPoints, extraProps, rootModuleID} =
|
|
170
|
+
useMemo(() => {
|
|
171
|
+
return prepareEntryPoint(
|
|
172
|
+
environmentProvider ?? {
|
|
173
|
+
getEnvironment: () => environment,
|
|
174
|
+
},
|
|
175
|
+
entryPoint,
|
|
176
|
+
entryPointParams,
|
|
177
|
+
);
|
|
178
|
+
// NOTE: stableParams encodes the information from params
|
|
179
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
180
|
+
}, [
|
|
181
|
+
environment,
|
|
182
|
+
environmentProvider,
|
|
183
|
+
getPreloadProps,
|
|
184
|
+
entryPointParamsHash,
|
|
185
|
+
]);
|
|
200
186
|
const Component = useMemo(() => {
|
|
201
187
|
return getComponent();
|
|
202
188
|
}, [getComponent]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
16
|
const React = require('react');
|
|
17
|
+
|
|
17
18
|
const {useMemo} = React;
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -90,7 +91,7 @@ type TypenameOnlyPointer = {|+__typename: string|};
|
|
|
90
91
|
export type MatchPointer = {
|
|
91
92
|
+__fragmentPropName?: ?string,
|
|
92
93
|
+__module_component?: mixed,
|
|
93
|
-
+$
|
|
94
|
+
+$fragmentSpreads: mixed,
|
|
94
95
|
...
|
|
95
96
|
};
|
|
96
97
|
|
|
@@ -115,7 +116,7 @@ function MatchContainer<TProps: {...}, TFallback: React.Node | null>({
|
|
|
115
116
|
'MatchContainer: Expected `match` value to be an object or null/undefined.',
|
|
116
117
|
);
|
|
117
118
|
}
|
|
118
|
-
// NOTE: the MatchPointer type has a $
|
|
119
|
+
// NOTE: the MatchPointer type has a $fragmentSpreads field to ensure that only
|
|
119
120
|
// an object that contains a FragmentSpread can be passed. If the fragment
|
|
120
121
|
// spread matches, then the metadata fields below (__id, __fragments, etc.)
|
|
121
122
|
// will be present. But they can be missing if all the fragment spreads use
|