react-relay 14.1.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 (196) hide show
  1. package/ReactRelayContainerUtils.js.flow +1 -0
  2. package/ReactRelayContext.js +1 -1
  3. package/ReactRelayContext.js.flow +1 -0
  4. package/ReactRelayFragmentContainer.js.flow +6 -2
  5. package/ReactRelayFragmentMockRenderer.js.flow +1 -0
  6. package/ReactRelayLocalQueryRenderer.js.flow +5 -3
  7. package/ReactRelayPaginationContainer.js.flow +21 -12
  8. package/ReactRelayQueryFetcher.js.flow +20 -10
  9. package/ReactRelayQueryRenderer.js.flow +15 -11
  10. package/ReactRelayQueryRendererContext.js.flow +1 -0
  11. package/ReactRelayRefetchContainer.js.flow +9 -5
  12. package/ReactRelayTestMocker.js.flow +3 -1
  13. package/ReactRelayTypes.js.flow +2 -0
  14. package/RelayContext.js.flow +1 -0
  15. package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +2 -1
  16. package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +1 -0
  17. package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +1 -0
  18. package/__flowtests__/RelayModern-flowtest.js.flow +1 -0
  19. package/__flowtests__/RelayModernFlowtest_badref.graphql.js.flow +1 -0
  20. package/__flowtests__/RelayModernFlowtest_notref.graphql.js.flow +1 -0
  21. package/__flowtests__/RelayModernFlowtest_user.graphql.js.flow +1 -0
  22. package/__flowtests__/RelayModernFlowtest_users.graphql.js.flow +1 -0
  23. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer.graphql.js.flow +3 -1
  24. package/__flowtests__/__generated__/ReactRelayFragmentContainerFlowtest_viewer2.graphql.js.flow +3 -1
  25. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtestQuery.graphql.js.flow +4 -2
  26. package/__flowtests__/__generated__/ReactRelayPaginationContainerFlowtest_viewer.graphql.js.flow +3 -1
  27. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtestQuery.graphql.js.flow +4 -2
  28. package/__flowtests__/__generated__/ReactRelayRefetchContainerFlowtest_viewer.graphql.js.flow +3 -1
  29. package/__flowtests__/__generated__/RelayModernFlowtest_badref.graphql.js.flow +4 -2
  30. package/__flowtests__/__generated__/RelayModernFlowtest_notref.graphql.js.flow +4 -2
  31. package/__flowtests__/__generated__/RelayModernFlowtest_user.graphql.js.flow +3 -1
  32. package/__flowtests__/__generated__/RelayModernFlowtest_users.graphql.js.flow +3 -1
  33. package/assertFragmentMap.js.flow +1 -0
  34. package/buildReactRelayContainer.js.flow +10 -6
  35. package/getRootVariablesForFragments.js.flow +1 -1
  36. package/hooks.js +1 -1
  37. package/hooks.js.flow +4 -0
  38. package/index.js +1 -1
  39. package/index.js.flow +4 -0
  40. package/isRelayEnvironment.js.flow +1 -0
  41. package/jest-react/enqueueTask.js.flow +1 -1
  42. package/jest-react/index.js.flow +1 -1
  43. package/jest-react/internalAct.js.flow +1 -1
  44. package/legacy.js +1 -1
  45. package/legacy.js.flow +1 -0
  46. package/lib/ReactRelayContainerUtils.js +0 -11
  47. package/lib/ReactRelayContext.js +1 -12
  48. package/lib/ReactRelayFragmentContainer.js +23 -122
  49. package/lib/ReactRelayFragmentMockRenderer.js +0 -12
  50. package/lib/ReactRelayLocalQueryRenderer.js +12 -41
  51. package/lib/ReactRelayPaginationContainer.js +45 -341
  52. package/lib/ReactRelayQueryFetcher.js +36 -111
  53. package/lib/ReactRelayQueryRenderer.js +29 -137
  54. package/lib/ReactRelayQueryRendererContext.js +0 -10
  55. package/lib/ReactRelayRefetchContainer.js +33 -166
  56. package/lib/ReactRelayTestMocker.js +18 -128
  57. package/lib/ReactRelayTypes.js +0 -9
  58. package/lib/RelayContext.js +0 -23
  59. package/lib/assertFragmentMap.js +0 -16
  60. package/lib/buildReactRelayContainer.js +7 -41
  61. package/lib/getRootVariablesForFragments.js +2 -19
  62. package/lib/hooks.js +3 -30
  63. package/lib/index.js +3 -39
  64. package/lib/isRelayEnvironment.js +1 -16
  65. package/lib/jest-react/enqueueTask.js +1 -25
  66. package/lib/jest-react/index.js +0 -1
  67. package/lib/jest-react/internalAct.js +2 -51
  68. package/lib/legacy.js +0 -20
  69. package/lib/multi-actor/ActorChange.js +0 -14
  70. package/lib/multi-actor/index.js +0 -10
  71. package/lib/multi-actor/useRelayActorEnvironment.js +2 -16
  72. package/lib/relay-hooks/EntryPointContainer.react.js +7 -23
  73. package/lib/relay-hooks/EntryPointTypes.flow.js +0 -10
  74. package/lib/relay-hooks/FragmentResource.js +130 -280
  75. package/lib/relay-hooks/HooksImplementation.js +0 -14
  76. package/lib/relay-hooks/InternalLogger.js +0 -11
  77. package/lib/relay-hooks/LRUCache.js +0 -39
  78. package/lib/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js +27 -65
  79. package/lib/relay-hooks/MatchContainer.js +9 -111
  80. package/lib/relay-hooks/NestedRelayEntryPointBuilderUtils.js +9 -0
  81. package/lib/relay-hooks/ProfilerContext.js +0 -14
  82. package/lib/relay-hooks/QueryResource.js +14 -149
  83. package/lib/relay-hooks/RelayEnvironmentProvider.js +3 -17
  84. package/lib/relay-hooks/SuspenseResource.js +2 -59
  85. package/lib/relay-hooks/loadEntryPoint.js +10 -45
  86. package/lib/relay-hooks/loadQuery.js +29 -169
  87. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +8 -58
  88. package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +6 -24
  89. package/lib/relay-hooks/react-cache/RelayReactCache.js +4 -20
  90. package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +13 -102
  91. package/lib/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js +18 -75
  92. package/lib/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js +79 -222
  93. package/lib/relay-hooks/react-cache/useFragment_REACT_CACHE.js +3 -27
  94. package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +11 -33
  95. package/lib/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js +62 -85
  96. package/lib/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js +20 -63
  97. package/lib/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js +53 -179
  98. package/lib/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js +5 -27
  99. package/lib/relay-hooks/useBlockingPaginationFragment.js +58 -121
  100. package/lib/relay-hooks/useClientQuery.js +0 -21
  101. package/lib/relay-hooks/useEntryPointLoader.js +12 -100
  102. package/lib/relay-hooks/useFetchTrackingRef.js +6 -33
  103. package/lib/relay-hooks/useFragment.js +5 -32
  104. package/lib/relay-hooks/useFragmentNode.js +14 -55
  105. package/lib/relay-hooks/useIsMountedRef.js +2 -14
  106. package/lib/relay-hooks/useIsOperationNodeActive.js +6 -29
  107. package/lib/relay-hooks/useIsParentQueryActive.js +1 -15
  108. package/lib/relay-hooks/useLazyLoadQuery.js +2 -23
  109. package/lib/relay-hooks/useLazyLoadQueryNode.js +18 -63
  110. package/lib/relay-hooks/useLoadMoreFunction.js +44 -100
  111. package/lib/relay-hooks/useMemoOperationDescriptor.js +4 -23
  112. package/lib/relay-hooks/useMemoVariables.js +8 -43
  113. package/lib/relay-hooks/useMutation.js +6 -34
  114. package/lib/relay-hooks/usePaginationFragment.js +49 -89
  115. package/lib/relay-hooks/usePreloadedQuery.js +10 -54
  116. package/lib/relay-hooks/useQueryLoader.js +18 -116
  117. package/lib/relay-hooks/useRefetchableFragment.js +4 -30
  118. package/lib/relay-hooks/useRefetchableFragmentNode.js +58 -184
  119. package/lib/relay-hooks/useRelayEnvironment.js +2 -16
  120. package/lib/relay-hooks/useStaticFragmentNodeWarning.js +2 -20
  121. package/lib/relay-hooks/useSubscribeToInvalidationState.js +3 -28
  122. package/lib/relay-hooks/useSubscription.js +3 -22
  123. package/lib/relay-hooks/useUnsafeRef_DEPRECATED.js +12 -0
  124. package/multi-actor/ActorChange.js.flow +1 -1
  125. package/multi-actor/index.js.flow +1 -1
  126. package/multi-actor/useRelayActorEnvironment.js.flow +2 -2
  127. package/package.json +2 -2
  128. package/react-relay-hooks.js +2 -2
  129. package/react-relay-hooks.min.js +2 -2
  130. package/react-relay-legacy.js +2 -2
  131. package/react-relay-legacy.min.js +2 -2
  132. package/react-relay.js +2 -2
  133. package/react-relay.min.js +2 -2
  134. package/relay-hooks/EntryPointContainer.react.js.flow +6 -1
  135. package/relay-hooks/EntryPointTypes.flow.js.flow +23 -20
  136. package/relay-hooks/FragmentResource.js.flow +148 -34
  137. package/relay-hooks/HooksImplementation.js.flow +1 -1
  138. package/relay-hooks/InternalLogger.js.flow +1 -1
  139. package/relay-hooks/LRUCache.js.flow +1 -1
  140. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +19 -10
  141. package/relay-hooks/MatchContainer.js.flow +1 -1
  142. package/relay-hooks/NestedRelayEntryPointBuilderUtils.js.flow +51 -0
  143. package/relay-hooks/ProfilerContext.js.flow +1 -1
  144. package/relay-hooks/QueryResource.js.flow +25 -5
  145. package/relay-hooks/RelayEnvironmentProvider.js.flow +2 -2
  146. package/relay-hooks/SuspenseResource.js.flow +1 -1
  147. package/relay-hooks/__flowtests__/EntryPointTypes/EntryPointElementConfig-flowtest.js.flow +3 -1
  148. package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +9 -7
  149. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_user.graphql.js.flow +3 -1
  150. package/relay-hooks/__flowtests__/__generated__/useFragmentFlowtest_users.graphql.js.flow +3 -1
  151. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +40 -33
  152. package/relay-hooks/__flowtests__/useFragment-flowtest.js.flow +1 -1
  153. package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +38 -32
  154. package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +20 -18
  155. package/relay-hooks/__flowtests__/utils.js.flow +13 -2
  156. package/relay-hooks/loadEntryPoint.js.flow +15 -8
  157. package/relay-hooks/loadQuery.js.flow +32 -8
  158. package/relay-hooks/preloadQuery_DEPRECATED.js.flow +5 -6
  159. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +17 -10
  160. package/relay-hooks/react-cache/RelayReactCache.js.flow +1 -1
  161. package/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js.flow +4 -4
  162. package/relay-hooks/react-cache/readFragmentInternal_REACT_CACHE.js.flow +5 -4
  163. package/relay-hooks/react-cache/useFragmentInternal_REACT_CACHE.js.flow +32 -14
  164. package/relay-hooks/react-cache/useFragment_REACT_CACHE.js.flow +4 -10
  165. package/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js.flow +1 -1
  166. package/relay-hooks/react-cache/usePaginationFragment_REACT_CACHE.js.flow +39 -49
  167. package/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js.flow +1 -2
  168. package/relay-hooks/react-cache/useRefetchableFragmentInternal_REACT_CACHE.js.flow +29 -16
  169. package/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js.flow +17 -33
  170. package/relay-hooks/useBlockingPaginationFragment.js.flow +85 -58
  171. package/relay-hooks/useClientQuery.js.flow +3 -3
  172. package/relay-hooks/useEntryPointLoader.js.flow +10 -6
  173. package/relay-hooks/useFetchTrackingRef.js.flow +5 -4
  174. package/relay-hooks/useFragment.js.flow +2 -2
  175. package/relay-hooks/useFragmentNode.js.flow +7 -6
  176. package/relay-hooks/useIsMountedRef.js.flow +1 -1
  177. package/relay-hooks/useIsOperationNodeActive.js.flow +1 -1
  178. package/relay-hooks/useIsParentQueryActive.js.flow +1 -1
  179. package/relay-hooks/useLazyLoadQuery.js.flow +2 -2
  180. package/relay-hooks/useLazyLoadQueryNode.js.flow +2 -2
  181. package/relay-hooks/useLoadMoreFunction.js.flow +27 -16
  182. package/relay-hooks/useMemoOperationDescriptor.js.flow +3 -3
  183. package/relay-hooks/useMemoVariables.js.flow +13 -29
  184. package/relay-hooks/useMutation.js.flow +30 -13
  185. package/relay-hooks/usePaginationFragment.js.flow +55 -54
  186. package/relay-hooks/usePreloadedQuery.js.flow +47 -22
  187. package/relay-hooks/useQueryLoader.js.flow +78 -21
  188. package/relay-hooks/useRefetchableFragment.js.flow +65 -33
  189. package/relay-hooks/useRefetchableFragmentNode.js.flow +38 -17
  190. package/relay-hooks/useRelayEnvironment.js.flow +2 -2
  191. package/relay-hooks/useStaticFragmentNodeWarning.js.flow +3 -3
  192. package/relay-hooks/useSubscribeToInvalidationState.js.flow +2 -2
  193. package/relay-hooks/useSubscription.js.flow +1 -1
  194. package/relay-hooks/useUnsafeRef_DEPRECATED.js.flow +25 -0
  195. package/lib/readContext.js +0 -27
  196. package/readContext.js.flow +0 -29
@@ -4,13 +4,13 @@
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.
6
6
  *
7
- * @emails oncall+relay
8
7
  * @flow strict-local
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
13
-
13
+ import type {OperationType} from '../../relay-runtime/util/RelayRuntimeTypes';
14
14
  import type {
15
15
  EntryPoint,
16
16
  EntryPointComponent,
@@ -40,12 +40,15 @@ function prepareEntryPoint<
40
40
  ): void {
41
41
  // Start loading the code for the entrypoint
42
42
  if (entryPoint.root.getModuleIfRequired() == null) {
43
+ // $FlowFixMe[unused-promise]
43
44
  entryPoint.root.load();
44
45
  }
45
46
  const preloadProps = entryPoint.getPreloadProps(entryPointParams);
46
47
  const {queries, entryPoints} = preloadProps;
47
- const preloadedQueries: $Shape<TPreloadedQueries> = {};
48
- const preloadedEntryPoints: $Shape<TPreloadedEntryPoints> = {};
48
+ // $FlowFixMe[incompatible-type]
49
+ const preloadedQueries: Partial<TPreloadedQueries> = {};
50
+ // $FlowFixMe[incompatible-type]
51
+ const preloadedEntryPoints: Partial<TPreloadedEntryPoints> = {};
49
52
  if (queries != null) {
50
53
  const queriesPropNames = Object.keys(queries);
51
54
  queriesPropNames.forEach(queryPropName => {
@@ -56,7 +59,7 @@ function prepareEntryPoint<
56
59
  environmentProviderOptions,
57
60
  );
58
61
 
59
- preloadedQueries[queryPropName] = preloadQuery(
62
+ preloadedQueries[queryPropName] = preloadQuery<OperationType, mixed>(
60
63
  environment,
61
64
  parameters,
62
65
  variables,
@@ -75,11 +78,15 @@ function prepareEntryPoint<
75
78
  }
76
79
  const {entryPoint: nestedEntryPoint, entryPointParams: nestedParams} =
77
80
  entryPointDescription;
78
- preloadedEntryPoints[entryPointPropName] = prepareEntryPoint(
79
- environmentProvider,
80
- nestedEntryPoint,
81
- nestedParams,
82
- );
81
+ preloadedEntryPoints[entryPointPropName] = prepareEntryPoint<
82
+ TEntryPointParams,
83
+ TPreloadedQueries,
84
+ TPreloadedEntryPoints,
85
+ TRuntimeProps,
86
+ TExtraProps,
87
+ TEntryPointComponent,
88
+ TEntryPoint,
89
+ >(environmentProvider, nestedEntryPoint, nestedParams);
83
90
  });
84
91
  }
85
92
  }
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -25,8 +25,8 @@ const SuspenseResource = require('../SuspenseResource');
25
25
  const {getCacheForType, getCacheSignal} = require('./RelayReactCache');
26
26
  const invariant = require('invariant');
27
27
  const {
28
- RelayFeatureFlags,
29
28
  __internal: {fetchQuery: fetchQueryInternal},
29
+ RelayFeatureFlags,
30
30
  } = require('relay-runtime');
31
31
  const warning = require('warning');
32
32
 
@@ -127,7 +127,7 @@ const noopOnCommit = () => {
127
127
  return () => undefined;
128
128
  };
129
129
 
130
- const noopPromise = new Promise(() => {});
130
+ const noopPromise = new Promise<void>(() => {});
131
131
 
132
132
  function getQueryCacheKey(
133
133
  operation: OperationDescriptor,
@@ -375,7 +375,7 @@ function executeOperationAndKeepUpToDate(
375
375
  customFetchObservable?: Observable<GraphQLResponse>,
376
376
  ) {
377
377
  let resolvePromise;
378
- const promise = new Promise(r => {
378
+ const promise = new Promise<void>(r => {
379
379
  resolvePromise = r;
380
380
  });
381
381
  // $FlowExpectedError[prop-missing] Expando to annotate Promises.
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -27,6 +27,7 @@ const {getQueryResourceForEnvironment} = require('../QueryResource');
27
27
  const invariant = require('invariant');
28
28
  const {
29
29
  __internal: {fetchQuery: fetchQueryInternal},
30
+ RelayFeatureFlags,
30
31
  createOperationDescriptor,
31
32
  getPendingOperationsForFragment,
32
33
  getSelector,
@@ -64,7 +65,7 @@ function getMissingClientEdges(
64
65
  } else if (state.kind === 'singular') {
65
66
  return state.snapshot.missingClientEdges ?? null;
66
67
  } else {
67
- let edges = null;
68
+ let edges: null | Array<MissingClientEdgeRequestInfo> = null;
68
69
  for (const snapshot of state.snapshots) {
69
70
  if (snapshot.missingClientEdges) {
70
71
  edges = edges ?? [];
@@ -223,7 +224,7 @@ function readFragmentInternal_REACT_CACHE(
223
224
  if (fragmentNode.metadata?.hasClientEdges === true) {
224
225
  const missingClientEdges = getMissingClientEdges(state);
225
226
  if (missingClientEdges?.length) {
226
- clientEdgeQueries = [];
227
+ clientEdgeQueries = ([]: Array<QueryResult>);
227
228
  for (const edge of missingClientEdges) {
228
229
  clientEdgeQueries.push(
229
230
  handleMissingClientEdge(
@@ -268,7 +269,7 @@ function readFragmentInternal_REACT_CACHE(
268
269
  data = state.snapshots.map(s => s.data);
269
270
  }
270
271
 
271
- if (__DEV__) {
272
+ if (RelayFeatureFlags.LOG_MISSING_RECORDS_IN_PROD || __DEV__) {
272
273
  if (
273
274
  fragmentRef != null &&
274
275
  (data === undefined ||
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -32,6 +32,7 @@ const invariant = require('invariant');
32
32
  const {useDebugValue, useEffect, useMemo, useRef, useState} = require('react');
33
33
  const {
34
34
  __internal: {fetchQuery: fetchQueryInternal},
35
+ RelayFeatureFlags,
35
36
  areEqualSelectors,
36
37
  createOperationDescriptor,
37
38
  getPendingOperationsForFragment,
@@ -73,7 +74,7 @@ function getMissingClientEdges(
73
74
  } else if (state.kind === 'singular') {
74
75
  return state.snapshot.missingClientEdges ?? null;
75
76
  } else {
76
- let edges = null;
77
+ let edges: null | Array<MissingClientEdgeRequestInfo> = null;
77
78
  for (const snapshot of state.snapshots) {
78
79
  if (snapshot.missingClientEdges) {
79
80
  edges = edges ?? [];
@@ -94,7 +95,7 @@ function getSuspendingLiveResolver(
94
95
  } else if (state.kind === 'singular') {
95
96
  return state.snapshot.missingLiveResolverFields ?? null;
96
97
  } else {
97
- let missingFields = null;
98
+ let missingFields: null | Array<MissingLiveResolverField> = null;
98
99
  for (const snapshot of state.snapshots) {
99
100
  if (snapshot.missingLiveResolverFields) {
100
101
  missingFields = missingFields ?? [];
@@ -256,7 +257,17 @@ function subscribeToSnapshot(
256
257
  prevState.kind !== 'singular' ||
257
258
  prevState.snapshot.selector !== latestSnapshot.selector
258
259
  ) {
259
- return prevState;
260
+ const updates = handleMissedUpdates(environment, prevState);
261
+ if (updates != null) {
262
+ const [dataChanged, nextState] = updates;
263
+ environment.__log({
264
+ name: 'useFragment.subscription.missedUpdates',
265
+ hasDataChanges: dataChanged,
266
+ });
267
+ return dataChanged ? nextState : prevState;
268
+ } else {
269
+ return prevState;
270
+ }
260
271
  }
261
272
  return {
262
273
  kind: 'singular',
@@ -279,7 +290,17 @@ function subscribeToSnapshot(
279
290
  prevState.kind !== 'plural' ||
280
291
  prevState.snapshots[index]?.selector !== latestSnapshot.selector
281
292
  ) {
282
- return prevState;
293
+ const updates = handleMissedUpdates(environment, prevState);
294
+ if (updates != null) {
295
+ const [dataChanged, nextState] = updates;
296
+ environment.__log({
297
+ name: 'useFragment.subscription.missedUpdates',
298
+ hasDataChanges: dataChanged,
299
+ });
300
+ return dataChanged ? nextState : prevState;
301
+ } else {
302
+ return prevState;
303
+ }
283
304
  }
284
305
  const updated = [...prevState.snapshots];
285
306
  updated[index] = latestSnapshot;
@@ -328,7 +349,6 @@ function useFragmentInternal_REACT_CACHE(
328
349
  fragmentRef: mixed,
329
350
  hookDisplayName: string,
330
351
  queryOptions?: FragmentQueryOptions,
331
- fragmentKey?: string,
332
352
  ): ?SelectorData | Array<?SelectorData> {
333
353
  const fragmentSelector = useMemo(
334
354
  () => getSelector(fragmentNode, fragmentRef),
@@ -343,7 +363,6 @@ function useFragmentInternal_REACT_CACHE(
343
363
  'Relay: Expected fragment pointer%s for fragment `%s` to be ' +
344
364
  'an array, instead got `%s`. Remove `@relay(plural: true)` ' +
345
365
  'from fragment `%s` to allow the prop to be an object.',
346
- fragmentKey != null ? ` for key \`${fragmentKey}\`` : '',
347
366
  fragmentNode.name,
348
367
  typeof fragmentRef,
349
368
  fragmentNode.name,
@@ -354,7 +373,6 @@ function useFragmentInternal_REACT_CACHE(
354
373
  'Relay: Expected fragment pointer%s for fragment `%s` not to be ' +
355
374
  'an array, instead got `%s`. Add `@relay(plural: true)` ' +
356
375
  'to fragment `%s` to allow the prop to be an array.',
357
- fragmentKey != null ? ` for key \`${fragmentKey}\`` : '',
358
376
  fragmentNode.name,
359
377
  typeof fragmentRef,
360
378
  fragmentNode.name,
@@ -378,7 +396,6 @@ function useFragmentInternal_REACT_CACHE(
378
396
  fragmentNode.name,
379
397
  hookDisplayName,
380
398
  fragmentNode.name,
381
- fragmentKey == null ? 'a fragment reference' : `the \`${fragmentKey}\``,
382
399
  hookDisplayName,
383
400
  );
384
401
 
@@ -438,7 +455,7 @@ function useFragmentInternal_REACT_CACHE(
438
455
  // eslint-disable-next-line no-shadow
439
456
  let clientEdgeQueries;
440
457
  if (missingClientEdges?.length) {
441
- clientEdgeQueries = [];
458
+ clientEdgeQueries = ([]: Array<QueryResult>);
442
459
  for (const edge of missingClientEdges) {
443
460
  clientEdgeQueries.push(
444
461
  handleMissingClientEdge(
@@ -505,11 +522,12 @@ function useFragmentInternal_REACT_CACHE(
505
522
  throw pendingOperationsResult.promise;
506
523
  }
507
524
  }
508
- // Report required fields only if we're not suspending, since that means
509
- // they're missing even though we are out of options for possibly fetching them:
510
- handlePotentialSnapshotErrorsForState(environment, state);
511
525
  }
512
526
 
527
+ // Report required fields only if we're not suspending, since that means
528
+ // they're missing even though we are out of options for possibly fetching them:
529
+ handlePotentialSnapshotErrorsForState(environment, state);
530
+
513
531
  useEffect(() => {
514
532
  // Check for updates since the state was rendered
515
533
  let currentState = subscribedState;
@@ -565,7 +583,7 @@ function useFragmentInternal_REACT_CACHE(
565
583
  data = state.snapshot.data;
566
584
  }
567
585
 
568
- if (__DEV__) {
586
+ if (RelayFeatureFlags.LOG_MISSING_RECORDS_IN_PROD || __DEV__) {
569
587
  if (
570
588
  fragmentRef != null &&
571
589
  (data === undefined ||
@@ -4,9 +4,9 @@
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.
6
6
  *
7
- * @emails oncall+relay
8
7
  * @flow strict-local
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -30,23 +30,17 @@ declare function useFragment<TFragmentType: FragmentType, TData>(
30
30
  key: HasSpread<TFragmentType>,
31
31
  ): TData;
32
32
 
33
- // if the key is nullable, return nullable value
34
- declare function useFragment<TFragmentType: FragmentType, TData>(
35
- fragment: Fragment<TFragmentType, TData>,
36
- key: ?HasSpread<TFragmentType>,
37
- ): ?TData;
38
-
39
33
  // if the key is a non-nullable array of keys, return non-nullable array
40
34
  declare function useFragment<TFragmentType: FragmentType, TData>(
41
35
  fragment: Fragment<TFragmentType, TData>,
42
36
  key: $ReadOnlyArray<HasSpread<TFragmentType>>,
43
37
  ): TData;
44
38
 
45
- // if the key is a nullable array of keys, return nullable array
39
+ // if the key is null/void, return null/void value
46
40
  declare function useFragment<TFragmentType: FragmentType, TData>(
47
41
  fragment: Fragment<TFragmentType, TData>,
48
- key: ?$ReadOnlyArray<HasSpread<TFragmentType>>,
49
- ): ?TData;
42
+ key: null | void,
43
+ ): null | void;
50
44
 
51
45
  function useFragment(fragment: GraphQLTaggedNode, key: mixed): mixed {
52
46
  // We need to use this hook in order to be able to track if
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -4,24 +4,26 @@
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.
6
6
  *
7
- * @emails oncall+relay
8
7
  * @flow strict-local
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
13
13
 
14
14
  import type {LoadMoreFn, UseLoadMoreFunctionArgs} from '../useLoadMoreFunction';
15
- import type {RefetchFnDynamic} from './useRefetchableFragmentInternal_REACT_CACHE';
15
+ import type {ReturnType} from '../usePaginationFragment';
16
+ import type {Options} from './useRefetchableFragmentInternal_REACT_CACHE';
16
17
  import type {
17
18
  FragmentType,
18
19
  GraphQLResponse,
19
- GraphQLTaggedNode,
20
20
  Observer,
21
- OperationType,
21
+ RefetchableFragment,
22
+ Variables,
22
23
  } from 'relay-runtime';
23
24
 
24
25
  const useLoadMoreFunction = require('../useLoadMoreFunction');
26
+ const useRelayEnvironment = require('../useRelayEnvironment');
25
27
  const useStaticFragmentNodeWarning = require('../useStaticFragmentNodeWarning');
26
28
  const useRefetchableFragmentInternal = require('./useRefetchableFragmentInternal_REACT_CACHE');
27
29
  const {useCallback, useDebugValue, useState} = require('react');
@@ -31,36 +33,15 @@ const {
31
33
  getPaginationMetadata,
32
34
  } = require('relay-runtime');
33
35
 
34
- export type ReturnType<TQuery: OperationType, TKey, TFragmentData> = {
35
- data: TFragmentData,
36
- loadNext: LoadMoreFn<TQuery>,
37
- loadPrevious: LoadMoreFn<TQuery>,
38
- hasNext: boolean,
39
- hasPrevious: boolean,
40
- isLoadingNext: boolean,
41
- isLoadingPrevious: boolean,
42
- refetch: RefetchFnDynamic<TQuery, TKey>,
43
- };
44
-
45
36
  function usePaginationFragment<
46
- TQuery: OperationType,
47
- TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
37
+ TFragmentType: FragmentType,
38
+ TVariables: Variables,
39
+ TData,
40
+ TKey: ?{+$fragmentSpreads: TFragmentType, ...},
48
41
  >(
49
- fragmentInput: GraphQLTaggedNode,
42
+ fragmentInput: RefetchableFragment<TFragmentType, TData, TVariables>,
50
43
  parentFragmentRef: TKey,
51
- ): ReturnType<
52
- TQuery,
53
- TKey,
54
- // NOTE: This $Call ensures that the type of the returned data is either:
55
- // - nullable if the provided ref type is nullable
56
- // - non-nullable if the provided ref type is non-nullable
57
- // prettier-ignore
58
- $Call<
59
- & (<TFragmentData>( { +$data?: TFragmentData, ... }) => TFragmentData)
60
- & (<TFragmentData>(?{ +$data?: TFragmentData, ... }) => ?TFragmentData),
61
- TKey,
62
- >,
63
- > {
44
+ ): ReturnType<TVariables, TData, TKey> {
64
45
  const fragmentNode = getFragment(fragmentInput);
65
46
  useStaticFragmentNodeWarning(
66
47
  fragmentNode,
@@ -68,22 +49,18 @@ function usePaginationFragment<
68
49
  );
69
50
  const componentDisplayName = 'usePaginationFragment()';
70
51
 
71
- const {
72
- connectionPathInFragmentData,
73
- paginationRequest,
74
- paginationMetadata,
75
- identifierField,
76
- } = getPaginationMetadata(fragmentNode, componentDisplayName);
52
+ const {connectionPathInFragmentData, paginationRequest, paginationMetadata} =
53
+ getPaginationMetadata(fragmentNode, componentDisplayName);
77
54
 
78
55
  const {fragmentData, fragmentRef, refetch} = useRefetchableFragmentInternal<
79
- TQuery,
80
- TKey,
56
+ {variables: TVariables, response: TData},
57
+ {data?: TData},
81
58
  >(fragmentNode, parentFragmentRef, componentDisplayName);
82
59
  const fragmentIdentifier = getFragmentIdentifier(fragmentNode, fragmentRef);
83
60
 
84
61
  // Backward pagination
85
62
  const [loadPrevious, hasPrevious, isLoadingPrevious, disposeFetchPrevious] =
86
- useLoadMore<TQuery>({
63
+ useLoadMore<TVariables>({
87
64
  componentDisplayName,
88
65
  connectionPathInFragmentData,
89
66
  direction: 'backward',
@@ -91,14 +68,13 @@ function usePaginationFragment<
91
68
  fragmentIdentifier,
92
69
  fragmentNode,
93
70
  fragmentRef,
94
- identifierField,
95
71
  paginationMetadata,
96
72
  paginationRequest,
97
73
  });
98
74
 
99
75
  // Forward pagination
100
76
  const [loadNext, hasNext, isLoadingNext, disposeFetchNext] =
101
- useLoadMore<TQuery>({
77
+ useLoadMore<TVariables>({
102
78
  componentDisplayName,
103
79
  connectionPathInFragmentData,
104
80
  direction: 'forward',
@@ -106,13 +82,12 @@ function usePaginationFragment<
106
82
  fragmentIdentifier,
107
83
  fragmentNode,
108
84
  fragmentRef,
109
- identifierField,
110
85
  paginationMetadata,
111
86
  paginationRequest,
112
87
  });
113
88
 
114
- const refetchPagination: RefetchFnDynamic<TQuery, TKey> = useCallback(
115
- (variables, options) => {
89
+ const refetchPagination = useCallback(
90
+ (variables: TVariables, options: void | Options) => {
116
91
  disposeFetchNext();
117
92
  disposeFetchPrevious();
118
93
  return refetch(variables, {...options, __environment: undefined});
@@ -132,6 +107,7 @@ function usePaginationFragment<
132
107
  });
133
108
  }
134
109
  return {
110
+ // $FlowFixMe[incompatible-return]
135
111
  data: fragmentData,
136
112
  loadNext,
137
113
  loadPrevious,
@@ -143,7 +119,7 @@ function usePaginationFragment<
143
119
  };
144
120
  }
145
121
 
146
- function useLoadMore<TQuery: OperationType>(
122
+ function useLoadMore<TVariables: Variables>(
147
123
  args: $Diff<
148
124
  UseLoadMoreFunctionArgs,
149
125
  {
@@ -152,15 +128,29 @@ function useLoadMore<TQuery: OperationType>(
152
128
  ...
153
129
  },
154
130
  >,
155
- ): [LoadMoreFn<TQuery>, boolean, boolean, () => void] {
156
- const [isLoadingMore, setIsLoadingMore] = useState(false);
131
+ ): [LoadMoreFn<TVariables>, boolean, boolean, () => void] {
132
+ const environment = useRelayEnvironment();
133
+ const [isLoadingMore, reallySetIsLoadingMore] = useState(false);
134
+ // Schedule this update since it must be observed by components at the same
135
+ // batch as when hasNext changes. hasNext is read from the store and store
136
+ // updates are scheduled, so this must be scheduled too.
137
+ const setIsLoadingMore = (value: boolean) => {
138
+ const schedule = environment.getScheduler()?.schedule;
139
+ if (schedule) {
140
+ schedule(() => {
141
+ reallySetIsLoadingMore(value);
142
+ });
143
+ } else {
144
+ reallySetIsLoadingMore(value);
145
+ }
146
+ };
157
147
  const observer = {
158
148
  start: () => setIsLoadingMore(true),
159
149
  complete: () => setIsLoadingMore(false),
160
150
  error: () => setIsLoadingMore(false),
161
151
  };
162
152
  const handleReset = () => setIsLoadingMore(false);
163
- const [loadMore, hasMore, disposeFetch] = useLoadMoreFunction({
153
+ const [loadMore, hasMore, disposeFetch] = useLoadMoreFunction<TVariables>({
164
154
  ...args,
165
155
  observer,
166
156
  onReset: handleReset,
@@ -5,8 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  * @flow strict-local
8
- * @emails oncall+relay
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
@@ -122,7 +122,6 @@ function usePreloadedQuery_REACT_CACHE<TQuery: OperationType>(
122
122
 
123
123
  // Read the query's root fragment -- this may suspend.
124
124
  const {fragmentNode, fragmentRef} = queryResult;
125
- // $FlowExpectedError[incompatible-return] Is this a fixable incompatible-return?
126
125
  const data = useFragmentInternal(
127
126
  fragmentNode,
128
127
  fragmentRef,
@@ -4,13 +4,14 @@
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.
6
6
  *
7
- * @emails oncall+relay
8
7
  * @flow strict-local
9
8
  * @format
9
+ * @oncall relay
10
10
  */
11
11
 
12
12
  'use strict';
13
13
 
14
+ import type {RefetchableIdentifierInfo} from '../../../relay-runtime/util/ReaderNode';
14
15
  import type {LoaderFn} from '../useQueryLoader';
15
16
  import type {
16
17
  ConcreteRequest,
@@ -98,7 +99,7 @@ type RefetchFnExact<TQuery: OperationType, TOptions = Options> = RefetchFnBase<
98
99
  type RefetchFnInexact<
99
100
  TQuery: OperationType,
100
101
  TOptions = Options,
101
- > = RefetchFnBase<$Shape<VariablesOf<TQuery>>, TOptions>;
102
+ > = RefetchFnBase<$ReadOnly<Partial<VariablesOf<TQuery>>>, TOptions>;
102
103
 
103
104
  type Action =
104
105
  | {
@@ -171,7 +172,7 @@ function useRefetchableFragmentNode<
171
172
  componentDisplayName: string,
172
173
  ): ReturnType<TQuery, TKey, InternalOptions> {
173
174
  const parentEnvironment = useRelayEnvironment();
174
- const {refetchableRequest, fragmentRefPathInResponse, identifierField} =
175
+ const {refetchableRequest, fragmentRefPathInResponse, identifierInfo} =
175
176
  getRefetchMetadata(fragmentNode, componentDisplayName);
176
177
  const fragmentIdentifier = getFragmentIdentifier(
177
178
  fragmentNode,
@@ -204,8 +205,11 @@ function useRefetchableFragmentNode<
204
205
  const shouldReset =
205
206
  environment !== mirroredEnvironment ||
206
207
  fragmentIdentifier !== mirroredFragmentIdentifier;
207
- const [queryRef, loadQuery, disposeQuery] =
208
- useQueryLoader<TQuery>(refetchableRequest);
208
+ const [queryRef, loadQuery, disposeQuery] = useQueryLoader<
209
+ TQuery['variables'],
210
+ TQuery['response'],
211
+ TQuery['rawResponse'],
212
+ >((refetchableRequest: $FlowFixMe));
209
213
 
210
214
  let fragmentRef = parentFragmentRef;
211
215
  if (shouldReset) {
@@ -233,6 +237,7 @@ function useRefetchableFragmentNode<
233
237
  debugPreviousIDAndTypename = debugFunctions.getInitialIDAndType(
234
238
  refetchQuery.request.variables,
235
239
  fragmentRefPathInResponse,
240
+ identifierInfo?.identifierQueryVariableName,
236
241
  environment,
237
242
  );
238
243
  }
@@ -340,7 +345,7 @@ function useRefetchableFragmentNode<
340
345
  fragmentIdentifier,
341
346
  fragmentNode,
342
347
  fragmentRefPathInResponse,
343
- identifierField,
348
+ identifierInfo,
344
349
  loadQuery,
345
350
  parentFragmentRef,
346
351
  refetchableRequest,
@@ -374,20 +379,23 @@ function useRefetchFunction<TQuery: OperationType>(
374
379
  fragmentIdentifier: string,
375
380
  fragmentNode: ReaderFragment,
376
381
  fragmentRefPathInResponse: $ReadOnlyArray<string | number>,
377
- identifierField: ?string,
382
+ identifierInfo: ?RefetchableIdentifierInfo,
378
383
  loadQuery: LoaderFn<TQuery>,
379
384
  parentFragmentRef: mixed,
380
385
  refetchableRequest: ConcreteRequest,
381
386
  ): RefetchFn<TQuery, InternalOptions> {
382
387
  const isMountedRef = useIsMountedRef();
383
388
  const identifierValue =
384
- identifierField != null &&
389
+ identifierInfo?.identifierField != null &&
385
390
  fragmentData != null &&
386
391
  typeof fragmentData === 'object'
387
- ? fragmentData[identifierField]
392
+ ? fragmentData[identifierInfo.identifierField]
388
393
  : null;
389
394
  return useCallback(
390
- (providedRefetchVariables, options) => {
395
+ (
396
+ providedRefetchVariables: VariablesOf<TQuery>,
397
+ options: void | InternalOptions,
398
+ ) => {
391
399
  // Bail out and warn if we're trying to refetch after the component
392
400
  // has unmounted
393
401
  if (isMountedRef.current !== true) {
@@ -448,8 +456,10 @@ function useRefetchFunction<TQuery: OperationType>(
448
456
  // If the query needs an identifier value ('id' or similar) and one
449
457
  // was not explicitly provided, read it from the fragment data.
450
458
  if (
451
- identifierField != null &&
452
- !providedRefetchVariables.hasOwnProperty('id')
459
+ identifierInfo != null &&
460
+ !providedRefetchVariables.hasOwnProperty(
461
+ identifierInfo.identifierQueryVariableName,
462
+ )
453
463
  ) {
454
464
  // @refetchable fragments are guaranteed to have an `id` selection
455
465
  // if the type is Node, implements Node, or is @fetchable. Double-check
@@ -459,11 +469,13 @@ function useRefetchFunction<TQuery: OperationType>(
459
469
  false,
460
470
  'Relay: Expected result to have a string ' +
461
471
  '`%s` in order to refetch, got `%s`.',
462
- identifierField,
472
+ identifierInfo.identifierField,
463
473
  identifierValue,
464
474
  );
465
475
  }
466
- (refetchVariables: $FlowFixMe).id = identifierValue;
476
+ (refetchVariables: $FlowFixMe)[
477
+ identifierInfo.identifierQueryVariableName
478
+ ] = identifierValue;
467
479
  }
468
480
 
469
481
  const refetchQuery = createOperationDescriptor(
@@ -512,10 +524,11 @@ if (__DEV__) {
512
524
  getInitialIDAndType(
513
525
  memoRefetchVariables: ?Variables,
514
526
  fragmentRefPathInResponse: $ReadOnlyArray<string | number>,
527
+ identifierQueryVariableName: ?string,
515
528
  environment: IEnvironment,
516
529
  ): ?DebugIDandTypename {
517
530
  const {Record} = require('relay-runtime');
518
- const id = memoRefetchVariables?.id;
531
+ const id = memoRefetchVariables?.[identifierQueryVariableName ?? 'id'];
519
532
  if (
520
533
  fragmentRefPathInResponse.length !== 1 ||
521
534
  fragmentRefPathInResponse[0] !== 'node' ||
@@ -525,7 +538,7 @@ if (__DEV__) {
525
538
  }
526
539
  const recordSource = environment.getStore().getSource();
527
540
  const record = recordSource.get(id);
528
- const typename = record && Record.getType(record);
541
+ const typename = record == null ? null : Record.getType(record);
529
542
  if (typename == null) {
530
543
  return null;
531
544
  }