react-relay 15.0.0 → 16.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.
Files changed (119) hide show
  1. package/ReactRelayContext.js +1 -1
  2. package/ReactRelayQueryFetcher.js.flow +1 -5
  3. package/ReactRelayQueryRenderer.js.flow +9 -36
  4. package/ReactRelayTypes.js.flow +1 -0
  5. package/buildReactRelayContainer.js.flow +3 -1
  6. package/hooks.js +1 -1
  7. package/index.js +1 -1
  8. package/legacy.js +1 -1
  9. package/lib/ReactRelayContainerUtils.js +0 -11
  10. package/lib/ReactRelayContext.js +0 -11
  11. package/lib/ReactRelayFragmentContainer.js +6 -78
  12. package/lib/ReactRelayFragmentMockRenderer.js +0 -11
  13. package/lib/ReactRelayLocalQueryRenderer.js +0 -17
  14. package/lib/ReactRelayPaginationContainer.js +5 -208
  15. package/lib/ReactRelayQueryFetcher.js +2 -51
  16. package/lib/ReactRelayQueryRenderer.js +6 -94
  17. package/lib/ReactRelayQueryRendererContext.js +0 -11
  18. package/lib/ReactRelayRefetchContainer.js +5 -91
  19. package/lib/ReactRelayTestMocker.js +9 -85
  20. package/lib/ReactRelayTypes.js +0 -11
  21. package/lib/RelayContext.js +0 -21
  22. package/lib/assertFragmentMap.js +0 -15
  23. package/lib/buildReactRelayContainer.js +0 -19
  24. package/lib/getRootVariablesForFragments.js +0 -14
  25. package/lib/hooks.js +0 -15
  26. package/lib/index.js +0 -17
  27. package/lib/isRelayEnvironment.js +1 -18
  28. package/lib/jest-react/enqueueTask.js +0 -20
  29. package/lib/jest-react/internalAct.js +0 -38
  30. package/lib/legacy.js +0 -15
  31. package/lib/multi-actor/ActorChange.js +0 -11
  32. package/lib/multi-actor/index.js +0 -11
  33. package/lib/multi-actor/useRelayActorEnvironment.js +0 -11
  34. package/lib/relay-hooks/EntryPointContainer.react.js +0 -11
  35. package/lib/relay-hooks/EntryPointTypes.flow.js +1 -14
  36. package/lib/relay-hooks/FragmentResource.js +76 -132
  37. package/lib/relay-hooks/HooksImplementation.js +0 -11
  38. package/lib/relay-hooks/InternalLogger.js +0 -11
  39. package/lib/relay-hooks/LRUCache.js +0 -22
  40. package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +0 -18
  41. package/lib/relay-hooks/MatchContainer.js +0 -94
  42. package/lib/relay-hooks/NestedRelayEntryPointBuilderUtils.js +9 -0
  43. package/lib/relay-hooks/ProfilerContext.js +0 -15
  44. package/lib/relay-hooks/QueryResource.js +2 -68
  45. package/lib/relay-hooks/RelayEnvironmentProvider.js +0 -11
  46. package/lib/relay-hooks/SuspenseResource.js +0 -34
  47. package/lib/relay-hooks/loadEntryPoint.js +1 -24
  48. package/lib/relay-hooks/loadQuery.js +2 -106
  49. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +2 -27
  50. package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +0 -13
  51. package/lib/relay-hooks/react-cache/RelayReactCache.js +0 -12
  52. package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +1 -36
  53. package/lib/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js +3 -27
  54. package/lib/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js +34 -99
  55. package/lib/relay-hooks/react-cache/useFragment_REACT_CACHE.js +0 -15
  56. package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +0 -16
  57. package/lib/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js +1 -23
  58. package/lib/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js +0 -29
  59. package/lib/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js +12 -96
  60. package/lib/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js +0 -14
  61. package/lib/relay-hooks/useBlockingPaginationFragment.js +0 -42
  62. package/lib/relay-hooks/useClientQuery.js +0 -18
  63. package/lib/relay-hooks/useEntryPointLoader.js +0 -69
  64. package/lib/relay-hooks/useFetchTrackingRef.js +0 -26
  65. package/lib/relay-hooks/useFragment.js +0 -17
  66. package/lib/relay-hooks/useFragmentNode.js +2 -32
  67. package/lib/relay-hooks/useIsMountedRef.js +0 -11
  68. package/lib/relay-hooks/useIsOperationNodeActive.js +0 -11
  69. package/lib/relay-hooks/useIsParentQueryActive.js +0 -11
  70. package/lib/relay-hooks/useLazyLoadQuery.js +0 -18
  71. package/lib/relay-hooks/useLazyLoadQueryNode.js +0 -35
  72. package/lib/relay-hooks/useLoadMoreFunction.js +9 -34
  73. package/lib/relay-hooks/useMemoOperationDescriptor.js +0 -11
  74. package/lib/relay-hooks/useMemoVariables.js +0 -17
  75. package/lib/relay-hooks/useMutation.js +0 -11
  76. package/lib/relay-hooks/usePaginationFragment.js +1 -26
  77. package/lib/relay-hooks/usePreloadedQuery.js +0 -27
  78. package/lib/relay-hooks/useQueryLoader.js +0 -74
  79. package/lib/relay-hooks/useRefetchableFragment.js +0 -16
  80. package/lib/relay-hooks/useRefetchableFragmentNode.js +14 -97
  81. package/lib/relay-hooks/useRelayEnvironment.js +0 -11
  82. package/lib/relay-hooks/useStaticFragmentNodeWarning.js +0 -15
  83. package/lib/relay-hooks/useSubscribeToInvalidationState.js +0 -25
  84. package/lib/relay-hooks/useSubscription.js +0 -15
  85. package/lib/relay-hooks/useUnsafeRef_DEPRECATED.js +0 -17
  86. package/package.json +2 -2
  87. package/react-relay-hooks.js +2 -2
  88. package/react-relay-hooks.min.js +2 -2
  89. package/react-relay-legacy.js +2 -2
  90. package/react-relay-legacy.min.js +2 -2
  91. package/react-relay.js +2 -2
  92. package/react-relay.min.js +2 -2
  93. package/relay-hooks/EntryPointContainer.react.js.flow +5 -0
  94. package/relay-hooks/EntryPointTypes.flow.js.flow +20 -19
  95. package/relay-hooks/FragmentResource.js.flow +114 -26
  96. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +4 -2
  97. package/relay-hooks/NestedRelayEntryPointBuilderUtils.js.flow +51 -0
  98. package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +7 -5
  99. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +5 -0
  100. package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +5 -0
  101. package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +2 -0
  102. package/relay-hooks/loadEntryPoint.js.flow +4 -2
  103. package/relay-hooks/loadQuery.js.flow +21 -1
  104. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +4 -2
  105. package/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js.flow +2 -1
  106. package/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js.flow +28 -10
  107. package/relay-hooks/react-cache/useFragment_REACT_CACHE.js.flow +3 -9
  108. package/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js.flow +28 -57
  109. package/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js.flow +19 -12
  110. package/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js.flow +15 -31
  111. package/relay-hooks/useBlockingPaginationFragment.js.flow +2 -4
  112. package/relay-hooks/useClientQuery.js.flow +2 -2
  113. package/relay-hooks/useFragmentNode.js.flow +2 -2
  114. package/relay-hooks/useLoadMoreFunction.js.flow +15 -9
  115. package/relay-hooks/useMutation.js.flow +26 -9
  116. package/relay-hooks/usePaginationFragment.js.flow +2 -8
  117. package/relay-hooks/useQueryLoader.js.flow +2 -8
  118. package/relay-hooks/useRefetchableFragment.js.flow +3 -2
  119. package/relay-hooks/useRefetchableFragmentNode.js.flow +28 -13
@@ -1,14 +1,3 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];
@@ -20,10 +9,7 @@ var _require = require('relay-runtime'),
20
9
  createOperationDescriptor = _require.createOperationDescriptor,
21
10
  getRequest = _require.getRequest,
22
11
  getRequestIdentifier = _require.getRequestIdentifier;
23
-
24
- // Expire results by this delay after they resolve.
25
- var DEFAULT_PREFETCH_TIMEOUT = 30 * 1000; // 30 seconds
26
-
12
+ var DEFAULT_PREFETCH_TIMEOUT = 30 * 1000;
27
13
  var WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
28
14
  var STORE_OR_NETWORK_DEFAULT = 'store-or-network';
29
15
  var pendingQueriesByEnvironment = WEAKMAP_SUPPORTED ? new WeakMap() : new Map();
@@ -33,7 +19,7 @@ function preloadQuery(environment, preloadableRequest, variables, options, envir
33
19
  _pendingQueries = new Map();
34
20
  pendingQueriesByEnvironment.set(environment, _pendingQueries);
35
21
  }
36
- var pendingQueries = _pendingQueries; // store in a const for flow
22
+ var pendingQueries = _pendingQueries;
37
23
  var queryEntry = preloadQueryDeduped(environment, pendingQueries, preloadableRequest, variables, options);
38
24
  var source = queryEntry.kind === 'network' ? Observable.create(function (sink) {
39
25
  var subscription;
@@ -52,8 +38,6 @@ function preloadQuery(environment, preloadableRequest, variables, options, envir
52
38
  return;
53
39
  }
54
40
  setTimeout(function () {
55
- // Clear the cache entry after the default timeout
56
- // null-check for Flow
57
41
  if (queryEntry != null) {
58
42
  cleanup(pendingQueries, queryEntry);
59
43
  }
@@ -115,15 +99,12 @@ function preloadQueryDeduped(environment, pendingQueries, preloadableRequest, va
115
99
  };
116
100
  if (!environment.isServer() && prevQueryEntry == null) {
117
101
  setTimeout(function () {
118
- // Clear the cache entry after the default timeout
119
- // null-check for Flow
120
102
  if (nextQueryEntry != null) {
121
103
  cleanup(pendingQueries, nextQueryEntry);
122
104
  }
123
105
  }, DEFAULT_PREFETCH_TIMEOUT);
124
106
  }
125
107
  } else if (prevQueryEntry == null || prevQueryEntry.kind !== 'network') {
126
- // Should fetch but we're not already fetching: fetch!
127
108
  var source = network.execute(params, variables, networkCacheConfig, null);
128
109
  var subject = new ReplaySubject();
129
110
  nextQueryEntry = {
@@ -144,8 +125,6 @@ function preloadQueryDeduped(environment, pendingQueries, preloadableRequest, va
144
125
  return;
145
126
  }
146
127
  setTimeout(function () {
147
- // Clear the cache entry after the default timeout
148
- // null-check for Flow
149
128
  if (nextQueryEntry != null) {
150
129
  cleanup(pendingQueries, nextQueryEntry);
151
130
  }
@@ -169,10 +148,6 @@ function preloadQueryDeduped(environment, pendingQueries, preloadableRequest, va
169
148
  return nextQueryEntry;
170
149
  }
171
150
  function cleanup(pendingQueries, entry) {
172
- // Reload the entry by its cache key and only invalidate if its the identical
173
- // entry instance. This ensures that if the same query/variables are fetched
174
- // successively that a timeout/expiration from an earlier fetch doesn't clear
175
- // a subsequent fetch.
176
151
  var currentEntry = pendingQueries.get(entry.cacheKey);
177
152
  if (currentEntry != null && currentEntry === entry) {
178
153
  if (currentEntry.kind === 'network') {
@@ -1,21 +1,8 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var preloadQuery = require('./preloadQuery_DEPRECATED');
15
4
  function prepareEntryPoint(environmentProvider, entryPoint, entryPointParams) {
16
- // Start loading the code for the entrypoint
17
5
  if (entryPoint.root.getModuleIfRequired() == null) {
18
- // $FlowFixMe[unused-promise]
19
6
  entryPoint.root.load();
20
7
  }
21
8
  var preloadProps = entryPoint.getPreloadProps(entryPointParams);
@@ -1,18 +1,6 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var invariant = require('invariant');
15
- // $FlowFixMe[prop-missing] These exist in experimental builds but aren't in React's types yet.
16
4
  var _require = require('react'),
17
5
  unstable_getCacheForType = _require.unstable_getCacheForType,
18
6
  unstable_getCacheSignal = _require.unstable_getCacheSignal;
@@ -1,14 +1,3 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];
@@ -102,15 +91,9 @@ function getQueryResultOrFetchQuery_REACT_CACHE(environment, queryOperationDescr
102
91
  currentEntry = makeInitialCacheEntry();
103
92
  cache.set(environment, cacheKey, currentEntry);
104
93
  }
105
- // $FlowExpectedError[prop-missing] Extra properties are passed in -- this is fine
106
94
  var newStatus = updater(currentEntry);
107
- // $FlowExpectedError[cannot-spread-inexact] Flow cannot understand that this is valid...
108
95
  cache.set(environment, cacheKey, (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, currentEntry), newStatus));
109
- // ... but we can because QueryCacheEntry spreads QueryCacheEntryStatus, so spreading
110
- // a QueryCacheEntryStatus into a QueryCacheEntry will result in a valid QueryCacheEntry.
111
96
  }
112
-
113
- // Initiate a query to fetch the data if needed:
114
97
  if (RelayFeatureFlags.USE_REACT_CACHE_LEGACY_TIMEOUTS) {
115
98
  var _entry;
116
99
  if (initialEntry === undefined) {
@@ -144,13 +127,7 @@ function getQueryResultOrFetchQuery_REACT_CACHE(environment, queryOperationDescr
144
127
  _entry.suspenseResource.temporaryRetain(environment);
145
128
  } else {
146
129
  if (initialEntry === undefined) {
147
- // This is the behavior we eventually want: We retain the query until the
148
- // presiding Cache component unmounts, at which point the AbortSignal
149
- // will be triggered.
150
130
  onCacheMiss(environment, queryOperationDescriptor, fetchPolicy, renderPolicy, updateCache, options === null || options === void 0 ? void 0 : options.fetchObservable);
151
-
152
- // Since this is the first time rendering, retain the query. React will
153
- // trigger the abort signal when this cache entry is no longer needed.
154
131
  var retention = environment.retain(queryOperationDescriptor);
155
132
  var dispose = function dispose() {
156
133
  retention.dispose();
@@ -162,7 +139,7 @@ function getQueryResultOrFetchQuery_REACT_CACHE(environment, queryOperationDescr
162
139
  });
163
140
  }
164
141
  }
165
- var entry = cache.get(environment, cacheKey); // could be a different entry now if synchronously resolved
142
+ var entry = cache.get(environment, cacheKey);
166
143
  !(entry !== undefined) ? process.env.NODE_ENV !== "production" ? invariant(false, 'An entry should have been created by onCacheMiss. This is a bug in Relay.') : invariant(false) : void 0;
167
144
  switch (entry.status) {
168
145
  case 'pending':
@@ -175,8 +152,6 @@ function getQueryResultOrFetchQuery_REACT_CACHE(environment, queryOperationDescr
175
152
  !false ? process.env.NODE_ENV !== "production" ? invariant(false, 'switch statement should be exhaustive') : invariant(false) : void 0;
176
153
  }
177
154
  function onCacheMiss(environment, operation, fetchPolicy, renderPolicy, updateCache, customFetchObservable) {
178
- // NB: Besides checking if the data is available, calling `check` will write missing
179
- // data to the store using any missing data handlers specified in the environment.
180
155
  var queryAvailability = environment.check(operation);
181
156
  var queryStatus = queryAvailability.status;
182
157
  var hasFullQuery = queryStatus === 'available';
@@ -240,11 +215,8 @@ function executeOperationAndKeepUpToDate(environment, operation, updateCache, cu
240
215
  var promise = new Promise(function (r) {
241
216
  resolvePromise = r;
242
217
  });
243
- // $FlowExpectedError[prop-missing] Expando to annotate Promises.
244
218
  promise.displayName = 'Relay(' + operation.request.node.operation.name + ')';
245
219
  var isFirstPayload = true;
246
-
247
- // FIXME We may still need to cancel network requests for live queries.
248
220
  var fetchObservable = customFetchObservable !== null && customFetchObservable !== void 0 ? customFetchObservable : fetchQueryInternal(environment, operation);
249
221
  fetchObservable.subscribe({
250
222
  start: function start(subscription) {},
@@ -257,15 +229,12 @@ function executeOperationAndKeepUpToDate(environment, operation, updateCache, cu
257
229
  };
258
230
  });
259
231
  } else {
260
- // TODO:T92030819 Remove this warning and actually throw the network error
261
- // To complete this task we need to have a way of precisely tracking suspendable points
262
232
  process.env.NODE_ENV !== "production" ? warning(false, 'getQueryResultOrFetchQuery: An incremental payload for query `%` returned an error: `%`:`%`.', operation.request.node.operation.name, _error.message, _error.stack) : void 0;
263
233
  }
264
234
  resolvePromise();
265
235
  isFirstPayload = false;
266
236
  },
267
237
  next: function next(response) {
268
- // Stop suspending on the first payload because of streaming, defer, etc.
269
238
  updateCache(function (_existing) {
270
239
  return {
271
240
  status: 'resolved',
@@ -276,10 +245,6 @@ function executeOperationAndKeepUpToDate(environment, operation, updateCache, cu
276
245
  isFirstPayload = false;
277
246
  }
278
247
  });
279
-
280
- // If the above subscription yields a value synchronously, then one of the updates
281
- // above will have already happened and we'll now be in a resolved or rejected state.
282
- // But in the usual case, we save the promise to the entry here:
283
248
  updateCache(function (existing) {
284
249
  return existing.status === 'pending' ? {
285
250
  status: 'pending',
@@ -1,14 +1,3 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];
@@ -19,6 +8,7 @@ var _require = require('../QueryResource'),
19
8
  var invariant = require('invariant');
20
9
  var _require2 = require('relay-runtime'),
21
10
  fetchQueryInternal = _require2.__internal.fetchQuery,
11
+ RelayFeatureFlags = _require2.RelayFeatureFlags,
22
12
  createOperationDescriptor = _require2.createOperationDescriptor,
23
13
  getPendingOperationsForFragment = _require2.getPendingOperationsForFragment,
24
14
  getSelector = _require2.getSelector,
@@ -95,14 +85,9 @@ function handlePotentialSnapshotErrorsForState(environment, state) {
95
85
  function handleMissingClientEdge(environment, parentFragmentNode, parentFragmentRef, missingClientEdgeRequestInfo, queryOptions) {
96
86
  var originalVariables = getVariablesFromFragment(parentFragmentNode, parentFragmentRef);
97
87
  var variables = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, originalVariables), {}, {
98
- id: missingClientEdgeRequestInfo.clientEdgeDestinationID // TODO should be a reserved name
88
+ id: missingClientEdgeRequestInfo.clientEdgeDestinationID
99
89
  });
100
-
101
90
  var queryOperationDescriptor = createOperationDescriptor(missingClientEdgeRequestInfo.request, variables, queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.networkCacheConfig);
102
- // This may suspend. We don't need to do anything with the results; all we're
103
- // doing here is started the query if needed and retaining and releasing it
104
- // according to the component mount/suspense cycle; QueryResource
105
- // already handles this by itself.
106
91
  var QueryResource = getQueryResourceForEnvironment(environment);
107
92
  return QueryResource.prepare(queryOperationDescriptor, fetchQueryInternal(environment, queryOperationDescriptor), queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.fetchPolicy);
108
93
  }
@@ -133,8 +118,6 @@ function getFragmentState(environment, fragmentSelector) {
133
118
  };
134
119
  }
135
120
  }
136
-
137
- // fragmentNode cannot change during the lifetime of the component, though fragmentRef may change.
138
121
  function readFragmentInternal_REACT_CACHE(environment, fragmentNode, fragmentRef, hookDisplayName, queryOptions, fragmentKey) {
139
122
  var _fragmentNode$metadat, _fragmentNode$metadat2;
140
123
  var fragmentSelector = getSelector(fragmentNode, fragmentRef);
@@ -146,9 +129,6 @@ function readFragmentInternal_REACT_CACHE(environment, fragmentNode, fragmentRef
146
129
  }
147
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. ' + 'In this case, pass `null` if the conditions for evaluating the ' + 'fragment are not met (e.g. if the `@include(if)` value is false.)', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, fragmentKey == null ? 'a fragment reference' : "the `".concat(fragmentKey, "`"), hookDisplayName) : invariant(false) : void 0;
148
131
  var state = getFragmentState(environment, fragmentSelector);
149
-
150
- // Handle the queries for any missing client edges; this may suspend.
151
- // FIXME handle client edges in parallel.
152
132
  var clientEdgeQueries = null;
153
133
  if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true) {
154
134
  var missingClientEdges = getMissingClientEdges(state);
@@ -169,16 +149,12 @@ function readFragmentInternal_REACT_CACHE(environment, fragmentNode, fragmentRef
169
149
  }
170
150
  }
171
151
  if (isMissingData(state)) {
172
- // Suspend if an active operation bears on this fragment, either the
173
- // fragment's owner or some other mutation etc. that could affect it:
174
152
  !(fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'refinement, see invariants above') : invariant(false) : void 0;
175
153
  var fragmentOwner = fragmentSelector.kind === 'PluralReaderSelector' ? fragmentSelector.selectors[0].owner : fragmentSelector.owner;
176
154
  var pendingOperationsResult = getPendingOperationsForFragment(environment, fragmentNode, fragmentOwner);
177
155
  if (pendingOperationsResult) {
178
156
  throw pendingOperationsResult.promise;
179
157
  }
180
- // Report required fields only if we're not suspending, since that means
181
- // they're missing even though we are out of options for possibly fetching them:
182
158
  handlePotentialSnapshotErrorsForState(environment, state);
183
159
  }
184
160
  var data;
@@ -191,7 +167,7 @@ function readFragmentInternal_REACT_CACHE(environment, fragmentNode, fragmentRef
191
167
  return s.data;
192
168
  });
193
169
  }
194
- if (process.env.NODE_ENV !== "production") {
170
+ if (RelayFeatureFlags.LOG_MISSING_RECORDS_IN_PROD || process.env.NODE_ENV !== "production") {
195
171
  if (fragmentRef != null && (data === undefined || Array.isArray(data) && data.length > 0 && data.every(function (d) {
196
172
  return d === undefined;
197
173
  }))) {
@@ -1,14 +1,3 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")["default"];
@@ -27,6 +16,7 @@ var _require2 = require('react'),
27
16
  useState = _require2.useState;
28
17
  var _require3 = require('relay-runtime'),
29
18
  fetchQueryInternal = _require3.__internal.fetchQuery,
19
+ RelayFeatureFlags = _require3.RelayFeatureFlags,
30
20
  areEqualSelectors = _require3.areEqualSelectors,
31
21
  createOperationDescriptor = _require3.createOperationDescriptor,
32
22
  getPendingOperationsForFragment = _require3.getPendingOperationsForFragment,
@@ -140,22 +130,14 @@ function handlePotentialSnapshotErrorsForState(environment, state) {
140
130
  }
141
131
  }
142
132
  }
143
-
144
- /**
145
- * Check for updates to the store that occurred concurrently with rendering the given `state` value,
146
- * returning a new (updated) state if there were updates or null if there were no changes.
147
- */
148
133
  function handleMissedUpdates(environment, state) {
149
134
  if (state.kind === 'bailout') {
150
135
  return null;
151
136
  }
152
- // FIXME this is invalid if we've just switched environments.
153
137
  var currentEpoch = environment.getStore().getEpoch();
154
138
  if (currentEpoch === state.epoch) {
155
139
  return null;
156
140
  }
157
- // The store has updated since we rendered (without us being subscribed yet),
158
- // so check for any updates to the data we're rendering:
159
141
  if (state.kind === 'singular') {
160
142
  var currentSnapshot = environment.lookup(state.snapshot.selector);
161
143
  var updatedData = recycleNodesInto(state.snapshot.data, currentSnapshot.data);
@@ -207,14 +189,9 @@ function handleMissedUpdates(environment, state) {
207
189
  function handleMissingClientEdge(environment, parentFragmentNode, parentFragmentRef, missingClientEdgeRequestInfo, queryOptions) {
208
190
  var originalVariables = getVariablesFromFragment(parentFragmentNode, parentFragmentRef);
209
191
  var variables = (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, originalVariables), {}, {
210
- id: missingClientEdgeRequestInfo.clientEdgeDestinationID // TODO should be a reserved name
192
+ id: missingClientEdgeRequestInfo.clientEdgeDestinationID
211
193
  });
212
-
213
194
  var queryOperationDescriptor = createOperationDescriptor(missingClientEdgeRequestInfo.request, variables, queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.networkCacheConfig);
214
- // This may suspend. We don't need to do anything with the results; all we're
215
- // doing here is started the query if needed and retaining and releasing it
216
- // according to the component mount/suspense cycle; QueryResource
217
- // already handles this by itself.
218
195
  var QueryResource = getQueryResourceForEnvironment(environment);
219
196
  return QueryResource.prepare(queryOperationDescriptor, fetchQueryInternal(environment, queryOperationDescriptor), queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.fetchPolicy);
220
197
  }
@@ -224,11 +201,19 @@ function subscribeToSnapshot(environment, state, setState) {
224
201
  } else if (state.kind === 'singular') {
225
202
  var disposable = environment.subscribe(state.snapshot, function (latestSnapshot) {
226
203
  setState(function (prevState) {
227
- // In theory a setState from a subscription could be batched together
228
- // with a setState to change the fragment selector. Guard against this
229
- // by bailing out of the state update if the selector has changed.
230
204
  if (prevState.kind !== 'singular' || prevState.snapshot.selector !== latestSnapshot.selector) {
231
- return prevState;
205
+ var updates = handleMissedUpdates(environment, prevState);
206
+ if (updates != null) {
207
+ var dataChanged = updates[0],
208
+ nextState = updates[1];
209
+ environment.__log({
210
+ name: 'useFragment.subscription.missedUpdates',
211
+ hasDataChanges: dataChanged
212
+ });
213
+ return dataChanged ? nextState : prevState;
214
+ } else {
215
+ return prevState;
216
+ }
232
217
  }
233
218
  return {
234
219
  kind: 'singular',
@@ -245,11 +230,19 @@ function subscribeToSnapshot(environment, state, setState) {
245
230
  return environment.subscribe(snapshot, function (latestSnapshot) {
246
231
  setState(function (prevState) {
247
232
  var _prevState$snapshots$;
248
- // In theory a setState from a subscription could be batched together
249
- // with a setState to change the fragment selector. Guard against this
250
- // by bailing out of the state update if the selector has changed.
251
233
  if (prevState.kind !== 'plural' || ((_prevState$snapshots$ = prevState.snapshots[index]) === null || _prevState$snapshots$ === void 0 ? void 0 : _prevState$snapshots$.selector) !== latestSnapshot.selector) {
252
- return prevState;
234
+ var updates = handleMissedUpdates(environment, prevState);
235
+ if (updates != null) {
236
+ var dataChanged = updates[0],
237
+ nextState = updates[1];
238
+ environment.__log({
239
+ name: 'useFragment.subscription.missedUpdates',
240
+ hasDataChanges: dataChanged
241
+ });
242
+ return dataChanged ? nextState : prevState;
243
+ } else {
244
+ return prevState;
245
+ }
253
246
  }
254
247
  var updated = (0, _toConsumableArray2["default"])(prevState.snapshots);
255
248
  updated[index] = latestSnapshot;
@@ -283,8 +276,6 @@ function getFragmentState(environment, fragmentSelector) {
283
276
  kind: 'bailout'
284
277
  };
285
278
  } else if (fragmentSelector.kind === 'PluralReaderSelector') {
286
- // Note that if fragmentRef is an empty array, fragmentSelector will be null so we'll hit the above case.
287
- // Null is returned by getSelector if fragmentRef has no non-null items.
288
279
  return {
289
280
  kind: 'plural',
290
281
  snapshots: fragmentSelector.selectors.map(function (s) {
@@ -300,20 +291,18 @@ function getFragmentState(environment, fragmentSelector) {
300
291
  };
301
292
  }
302
293
  }
303
-
304
- // fragmentNode cannot change during the lifetime of the component, though fragmentRef may change.
305
- function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayName, queryOptions, fragmentKey) {
294
+ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayName, queryOptions) {
306
295
  var _fragmentNode$metadat, _fragmentNode$metadat2;
307
296
  var fragmentSelector = useMemo(function () {
308
297
  return getSelector(fragmentNode, fragmentRef);
309
298
  }, [fragmentNode, fragmentRef]);
310
299
  var isPlural = (fragmentNode === null || fragmentNode === void 0 ? void 0 : (_fragmentNode$metadat = fragmentNode.metadata) === null || _fragmentNode$metadat === void 0 ? void 0 : _fragmentNode$metadat.plural) === true;
311
300
  if (isPlural) {
312
- !(fragmentRef == null || Array.isArray(fragmentRef)) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected fragment pointer%s for fragment `%s` to be ' + 'an array, instead got `%s`. Remove `@relay(plural: true)` ' + 'from fragment `%s` to allow the prop to be an object.', fragmentKey != null ? " for key `".concat(fragmentKey, "`") : '', fragmentNode.name, typeof fragmentRef, fragmentNode.name) : invariant(false) : void 0;
301
+ !(fragmentRef == null || Array.isArray(fragmentRef)) ? process.env.NODE_ENV !== "production" ? invariant(false, 'Relay: Expected fragment pointer%s for fragment `%s` to be ' + 'an array, instead got `%s`. Remove `@relay(plural: true)` ' + 'from fragment `%s` to allow the prop to be an object.', fragmentNode.name, typeof fragmentRef, fragmentNode.name) : invariant(false) : void 0;
313
302
  } else {
314
- !!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;
303
+ !!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;
315
304
  }
316
- !(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. ' + 'In this case, pass `null` if the conditions for evaluating the ' + 'fragment are not met (e.g. if the `@include(if)` value is false.)', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, fragmentKey == null ? 'a fragment reference' : "the `".concat(fragmentKey, "`"), hookDisplayName) : invariant(false) : void 0;
305
+ !(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. ' + 'In this case, pass `null` if the conditions for evaluating the ' + 'fragment are not met (e.g. if the `@include(if)` value is false.)', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, hookDisplayName) : invariant(false) : void 0;
317
306
  var environment = useRelayEnvironment();
318
307
  var _useState = useState(function () {
319
308
  return getFragmentState(environment, fragmentSelector);
@@ -321,14 +310,9 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
321
310
  _state = _useState[0],
322
311
  setState = _useState[1];
323
312
  var state = _state;
324
-
325
- // This copy of the state we only update when something requires us to
326
- // unsubscribe and re-subscribe, namely a changed environment or
327
- // fragment selector.
328
313
  var _useState2 = useState(state),
329
314
  _subscribedState = _useState2[0],
330
315
  setSubscribedState = _useState2[1];
331
- // FIXME since this is used as an effect dependency, it needs to be memoized.
332
316
  var subscribedState = _subscribedState;
333
317
  var _useState3 = useState(fragmentSelector),
334
318
  previousFragmentSelector = _useState3[0],
@@ -337,40 +321,21 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
337
321
  previousEnvironment = _useState4[0],
338
322
  setPreviousEnvironment = _useState4[1];
339
323
  if (!areEqualSelectors(fragmentSelector, previousFragmentSelector) || environment !== previousEnvironment) {
340
- // Enqueue setState to record the new selector and state
341
324
  setPreviousFragmentSelector(fragmentSelector);
342
325
  setPreviousEnvironment(environment);
343
326
  var newState = getFragmentState(environment, fragmentSelector);
344
327
  setState(newState);
345
- setSubscribedState(newState); // This causes us to form a new subscription
346
- // But render with the latest state w/o waiting for the setState. Otherwise
347
- // the component would render the wrong information temporarily (including
348
- // possibly incorrectly triggering some warnings below).
328
+ setSubscribedState(newState);
349
329
  state = newState;
350
330
  subscribedState = newState;
351
331
  }
352
-
353
- // The purpose of this is to detect whether we have ever committed, because we
354
- // don't suspend on store updates, only when the component either is first trying
355
- // to mount or when the our selector changes. The selector change in particular is
356
- // how we suspend for pagination and refetech. Also, fragment selector can be null
357
- // or undefined, so we use false as a special value to distinguish from all fragment
358
- // selectors; false means that the component hasn't mounted yet.
359
332
  var committedFragmentSelectorRef = useRef(false);
360
333
  useEffect(function () {
361
334
  committedFragmentSelectorRef.current = fragmentSelector;
362
335
  }, [fragmentSelector]);
363
-
364
- // Handle the queries for any missing client edges; this may suspend.
365
- // FIXME handle client edges in parallel.
366
336
  if (((_fragmentNode$metadat2 = fragmentNode.metadata) === null || _fragmentNode$metadat2 === void 0 ? void 0 : _fragmentNode$metadat2.hasClientEdges) === true) {
367
- // The fragment is validated to be static (in useFragment) and hasClientEdges is
368
- // a static (constant) property of the fragment. In practice, this effect will
369
- // always or never run for a given invocation of this hook.
370
- // eslint-disable-next-line react-hooks/rules-of-hooks
371
337
  var clientEdgeQueries = useMemo(function () {
372
338
  var missingClientEdges = getMissingClientEdges(state);
373
- // eslint-disable-next-line no-shadow
374
339
  var clientEdgeQueries;
375
340
  if (missingClientEdges !== null && missingClientEdges !== void 0 && missingClientEdges.length) {
376
341
  clientEdgeQueries = [];
@@ -389,9 +354,6 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
389
354
  }
390
355
  return clientEdgeQueries;
391
356
  }, [state, environment, fragmentNode, fragmentRef, queryOptions]);
392
-
393
- // See above note
394
- // eslint-disable-next-line react-hooks/rules-of-hooks
395
357
  useEffect(function () {
396
358
  var QueryResource = getQueryResourceForEnvironment(environment);
397
359
  if (clientEdgeQueries !== null && clientEdgeQueries !== void 0 && clientEdgeQueries.length) {
@@ -426,19 +388,13 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
426
388
  }, [environment, clientEdgeQueries]);
427
389
  }
428
390
  if (isMissingData(state)) {
429
- // Suspend if a Live Resolver within this fragment is in a suspended state:
430
391
  var suspendingLiveResolvers = getSuspendingLiveResolver(state);
431
392
  if (suspendingLiveResolvers != null && suspendingLiveResolvers.length > 0) {
432
393
  throw Promise.all(suspendingLiveResolvers.map(function (_ref) {
433
394
  var liveStateID = _ref.liveStateID;
434
- // $FlowFixMe[prop-missing] This is expected to be a LiveResolverStore
435
395
  return environment.getStore().getLiveResolverPromise(liveStateID);
436
396
  }));
437
397
  }
438
- // Suspend if an active operation bears on this fragment, either the
439
- // fragment's owner or some other mutation etc. that could affect it.
440
- // We only suspend when the component is first trying to mount or changing
441
- // selectors, not if data becomes missing later:
442
398
  if (!committedFragmentSelectorRef.current || !areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector)) {
443
399
  !(fragmentSelector != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'refinement, see invariants above') : invariant(false) : void 0;
444
400
  var fragmentOwner = fragmentSelector.kind === 'PluralReaderSelector' ? fragmentSelector.selectors[0].owner : fragmentSelector.owner;
@@ -447,24 +403,14 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
447
403
  throw pendingOperationsResult.promise;
448
404
  }
449
405
  }
450
- // Report required fields only if we're not suspending, since that means
451
- // they're missing even though we are out of options for possibly fetching them:
452
- handlePotentialSnapshotErrorsForState(environment, state);
453
406
  }
407
+ handlePotentialSnapshotErrorsForState(environment, state);
454
408
  useEffect(function () {
455
- // Check for updates since the state was rendered
456
409
  var currentState = subscribedState;
457
410
  var updates = handleMissedUpdates(environment, subscribedState);
458
411
  if (updates !== null) {
459
412
  var didMissUpdates = updates[0],
460
413
  updatedState = updates[1];
461
- // TODO: didMissUpdates only checks for changes to snapshot data, but it's possible
462
- // that other snapshot properties may have changed that should also trigger a re-render,
463
- // such as changed missing resolver fields, missing client edges, etc.
464
- // A potential alternative is for handleMissedUpdates() to recycle the entire state
465
- // value, and return the new (recycled) state only if there was some change. In that
466
- // case the code would always setState if something in the snapshot changed, in addition
467
- // to using the latest snapshot to subscribe.
468
414
  if (didMissUpdates) {
469
415
  setState(updatedState);
470
416
  }
@@ -474,17 +420,9 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
474
420
  }, [environment, subscribedState]);
475
421
  var data;
476
422
  if (isPlural) {
477
- // Plural fragments require allocating an array of the snasphot data values,
478
- // which has to be memoized to avoid triggering downstream re-renders.
479
- //
480
- // Note that isPlural is a constant property of the fragment and does not change
481
- // for a particular useFragment invocation site
482
- var fragmentRefIsNullish = fragmentRef == null; // for less sensitive memoization
483
- // eslint-disable-next-line react-hooks/rules-of-hooks
423
+ var fragmentRefIsNullish = fragmentRef == null;
484
424
  data = useMemo(function () {
485
425
  if (state.kind === 'bailout') {
486
- // Bailout state can happen if the fragmentRef is a plural array that is empty or has no
487
- // non-null entries. In that case, the compatible behavior is to return [] instead of null.
488
426
  return fragmentRefIsNullish ? null : [];
489
427
  } else {
490
428
  !(state.kind === 'plural') ? process.env.NODE_ENV !== "production" ? invariant(false, 'Expected state to be plural because fragment is plural') : invariant(false) : void 0;
@@ -494,14 +432,12 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
494
432
  }
495
433
  }, [state, fragmentRefIsNullish]);
496
434
  } else if (state.kind === 'bailout') {
497
- // This case doesn't allocate a new object so it doesn't have to be memoized
498
435
  data = null;
499
436
  } else {
500
- // This case doesn't allocate a new object so it doesn't have to be memoized
501
437
  !(state.kind === 'singular') ? process.env.NODE_ENV !== "production" ? invariant(false, 'Expected state to be singular because fragment is singular') : invariant(false) : void 0;
502
438
  data = state.snapshot.data;
503
439
  }
504
- if (process.env.NODE_ENV !== "production") {
440
+ if (RelayFeatureFlags.LOG_MISSING_RECORDS_IN_PROD || process.env.NODE_ENV !== "production") {
505
441
  if (fragmentRef != null && (data === undefined || Array.isArray(data) && data.length > 0 && data.every(function (d) {
506
442
  return d === undefined;
507
443
  }))) {
@@ -509,7 +445,6 @@ function useFragmentInternal_REACT_CACHE(fragmentNode, fragmentRef, hookDisplayN
509
445
  }
510
446
  }
511
447
  if (process.env.NODE_ENV !== "production") {
512
- // eslint-disable-next-line react-hooks/rules-of-hooks
513
448
  useDebugValue({
514
449
  fragment: fragmentNode.name,
515
450
  data: data
@@ -1,14 +1,3 @@
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
- *
8
- * @format
9
- * @oncall relay
10
- */
11
-
12
1
  'use strict';
13
2
 
14
3
  var _require = require('../loadQuery'),
@@ -20,17 +9,13 @@ var _require2 = require('react'),
20
9
  var _require3 = require('relay-runtime'),
21
10
  getFragment = _require3.getFragment;
22
11
  function useFragment(fragment, key) {
23
- // We need to use this hook in order to be able to track if
24
- // loadQuery was called during render
25
12
  useTrackLoadQueryInRender();
26
13
  var fragmentNode = getFragment(fragment);
27
14
  if (process.env.NODE_ENV !== "production") {
28
- // eslint-disable-next-line react-hooks/rules-of-hooks
29
15
  useStaticFragmentNodeWarning(fragmentNode, 'first argument of useFragment()');
30
16
  }
31
17
  var data = useFragmentInternal(fragmentNode, key, 'useFragment()');
32
18
  if (process.env.NODE_ENV !== "production") {
33
- // eslint-disable-next-line react-hooks/rules-of-hooks
34
19
  useDebugValue({
35
20
  fragment: fragmentNode.name,
36
21
  data: data