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
@@ -85,8 +85,10 @@ function prepareEntryPoint<
85
85
  }
86
86
  const preloadProps = entryPoint.getPreloadProps(entryPointParams);
87
87
  const {queries, entryPoints, extraProps} = preloadProps;
88
- const preloadedQueries: $Shape<TPreloadedQueries> = {};
89
- const preloadedEntryPoints: $Shape<TPreloadedEntryPoints> = {};
88
+ // $FlowFixMe[incompatible-type]
89
+ const preloadedQueries: Partial<TPreloadedQueries> = {};
90
+ // $FlowFixMe[incompatible-type]
91
+ const preloadedEntryPoints: Partial<TPreloadedEntryPoints> = {};
90
92
  if (queries != null) {
91
93
  const queriesPropNames = Object.keys(queries);
92
94
  queriesPropNames.forEach(queryPropName => {
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @oncall relay
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ import type {
15
+ InternalEntryPointRepresentation,
16
+ ThinNestedEntryPointParams,
17
+ } from './EntryPointTypes.flow';
18
+
19
+ /**
20
+ * This is an identity function to construct a type safe nested entrypoint.
21
+ * By calling this function, we ensure that the type of entryPointParams matches
22
+ * exactly the type of preloadProps of the entrypoint.
23
+ *
24
+ * We make the type of `ThinNestedEntryPointParams` opaque, so that the only way
25
+ * to construct a `ThinNestedEntryPointParams` is by calling this function.
26
+ */
27
+ declare function NestedRelayEntryPoint<
28
+ TEntryPointParams,
29
+ TPreloadedQueries,
30
+ TPreloadedEntryPoints,
31
+ TRuntimeProps,
32
+ TExtraProps,
33
+ >(
34
+ $ReadOnly<{
35
+ entryPoint: InternalEntryPointRepresentation<
36
+ TEntryPointParams,
37
+ TPreloadedQueries,
38
+ TPreloadedEntryPoints,
39
+ TRuntimeProps,
40
+ TExtraProps,
41
+ >,
42
+ entryPointParams: TEntryPointParams,
43
+ }>,
44
+ ): ThinNestedEntryPointParams;
45
+
46
+ // eslint-disable-next-line no-redeclare
47
+ function NestedRelayEntryPoint<P>(params: P): P {
48
+ return params;
49
+ }
50
+
51
+ export {NestedRelayEntryPoint};
@@ -18,6 +18,8 @@ import type {
18
18
  } from '../../EntryPointTypes.flow';
19
19
  import type {JSResourceReference} from 'JSResourceReference';
20
20
 
21
+ import {NestedRelayEntryPoint} from '../../NestedRelayEntryPointBuilderUtils';
22
+
21
23
  declare function mockJSResource<TModule>(
22
24
  module: TModule,
23
25
  ): JSResourceReference<TModule>;
@@ -65,14 +67,14 @@ type BadParentEntrypointParams = $ReadOnly<{}>;
65
67
  getPreloadProps(_params: BadParentEntrypointParams) {
66
68
  return {
67
69
  entryPoints: {
68
- nestedComponent: {
69
- entryPoint: NestedEntryPoint,
70
+ nestedComponent: NestedRelayEntryPoint({
70
71
  /**
71
72
  $FlowExpectedError The entryPointParams here should be of type
72
73
  NestedEntrypointPreloadParams, but it does not contain subEntrypointPreloadParam
73
74
  */
75
+ entryPoint: NestedEntryPoint,
74
76
  entryPointParams: Object.freeze({}),
75
- },
77
+ }),
76
78
  },
77
79
  };
78
80
  },
@@ -90,13 +92,13 @@ type GoodParentEntrypointParams = $ReadOnly<{}>;
90
92
  getPreloadProps(_params: GoodParentEntrypointParams) {
91
93
  return {
92
94
  entryPoints: {
93
- nestedComponent: {
95
+ nestedComponent: NestedRelayEntryPoint({
94
96
  entryPoint: NestedEntryPoint,
95
97
  // No flow error since this matches NestedEntrypointPreloadParams
96
98
  entryPointParams: {
97
99
  subEntrypointPreloadParam: 'test',
98
100
  },
99
- },
101
+ }),
100
102
  },
101
103
  };
102
104
  },
@@ -45,6 +45,8 @@ type ExpectedReturnType<
45
45
  /* eslint-disable react-hooks/rules-of-hooks */
46
46
 
47
47
  // Nullability of returned data type is correct
48
+ // $FlowFixMe[prop-missing]
49
+ // $FlowFixMe[incompatible-cast]
48
50
  (useBlockingPaginationFragment(
49
51
  refetchableFragmentInput,
50
52
  keyNonNullable,
@@ -112,9 +114,12 @@ const {loadNext} = useBlockingPaginationFragment(
112
114
  );
113
115
  // Accepts extraVariables
114
116
  loadNext(10, {
117
+ // $FlowFixMe[prop-missing]
118
+ // $FlowFixMe[incompatible-call]
115
119
  UNSTABLE_extraVariables: extraVariables,
116
120
  });
117
121
 
122
+ // $FlowFixMe[prop-missing]
118
123
  loadNext(10, {
119
124
  // $FlowExpectedError: doesn't accept variables not available in the Flow type
120
125
  UNSTABLE_extraVariables: invalidVariables,
@@ -47,6 +47,8 @@ type ExpectedReturnType<
47
47
  /* eslint-disable react-hooks/rules-of-hooks */
48
48
 
49
49
  // Nullability of returned data type is correct
50
+ // $FlowFixMe[prop-missing]
51
+ // $FlowFixMe[incompatible-cast]
50
52
  (usePaginationFragment(
51
53
  refetchableFragmentInput,
52
54
  keyNonNullable,
@@ -113,9 +115,12 @@ const {loadNext} = usePaginationFragment(
113
115
  );
114
116
  // Accepts extraVariables
115
117
  loadNext(10, {
118
+ // $FlowFixMe[prop-missing]
119
+ // $FlowFixMe[incompatible-call]
116
120
  UNSTABLE_extraVariables: extraVariables,
117
121
  });
118
122
 
123
+ // $FlowFixMe[prop-missing]
119
124
  loadNext(10, {
120
125
  // $FlowExpectedError: doesn't accept variables not available in the Flow type
121
126
  UNSTABLE_extraVariables: invalidVariables,
@@ -31,6 +31,8 @@ import {
31
31
  /* eslint-disable react-hooks/rules-of-hooks */
32
32
 
33
33
  // Nullability of returned data type is correct
34
+ // $FlowFixMe[prop-missing]
35
+ // $FlowFixMe[incompatible-cast]
34
36
  (useRefetchableFragment(refetchableFragmentInput, keyNonNullable): [
35
37
  NonNullableData,
36
38
  FetchFn<QueryVariablesSubset>,
@@ -46,8 +46,10 @@ function loadEntryPoint<
46
46
  }
47
47
  const preloadProps = entryPoint.getPreloadProps(entryPointParams);
48
48
  const {queries, entryPoints, extraProps} = preloadProps;
49
- const preloadedQueries: $Shape<TPreloadedQueries> = {};
50
- const preloadedEntryPoints: $Shape<TPreloadedEntryPoints> = {};
49
+ // $FlowFixMe[incompatible-type]
50
+ const preloadedQueries: Partial<TPreloadedQueries> = {};
51
+ // $FlowFixMe[incompatible-type]
52
+ const preloadedEntryPoints: Partial<TPreloadedEntryPoints> = {};
51
53
  if (queries != null) {
52
54
  const queriesPropNames = Object.keys(queries);
53
55
  queriesPropNames.forEach(queryPropName => {
@@ -24,6 +24,7 @@ import type {
24
24
  IEnvironment,
25
25
  OperationDescriptor,
26
26
  OperationType,
27
+ Query,
27
28
  RequestIdentifier,
28
29
  RequestParameters,
29
30
  } from 'relay-runtime';
@@ -56,12 +57,31 @@ function useTrackLoadQueryInRender() {
56
57
  }
57
58
  }
58
59
 
60
+ type QueryType<T> = T extends Query<infer V, infer D, infer RR>
61
+ ? {
62
+ variables: V,
63
+ response: D,
64
+ rawResponse?: $NonMaybeType<RR>,
65
+ }
66
+ : $Call<<T>(PreloadableConcreteRequest<T>) => T, T>;
67
+
68
+ declare function loadQuery<
69
+ T,
70
+ TEnvironmentProviderOptions = EnvironmentProviderOptions,
71
+ >(
72
+ environment: IEnvironment,
73
+ preloadableRequest: T,
74
+ variables: QueryType<T>['variables'],
75
+ options?: ?LoadQueryOptions,
76
+ environmentProviderOptions?: ?TEnvironmentProviderOptions,
77
+ ): PreloadedQueryInner<QueryType<T>, TEnvironmentProviderOptions>;
78
+
59
79
  function loadQuery<
60
80
  TQuery: OperationType,
61
81
  TEnvironmentProviderOptions = EnvironmentProviderOptions,
62
82
  >(
63
83
  environment: IEnvironment,
64
- preloadableRequest: GraphQLTaggedNode | PreloadableConcreteRequest<TQuery>,
84
+ preloadableRequest: PreloadableConcreteRequest<TQuery>,
65
85
  variables: TQuery['variables'],
66
86
  options?: ?LoadQueryOptions,
67
87
  environmentProviderOptions?: ?TEnvironmentProviderOptions,
@@ -45,8 +45,10 @@ function prepareEntryPoint<
45
45
  }
46
46
  const preloadProps = entryPoint.getPreloadProps(entryPointParams);
47
47
  const {queries, entryPoints} = preloadProps;
48
- const preloadedQueries: $Shape<TPreloadedQueries> = {};
49
- const preloadedEntryPoints: $Shape<TPreloadedEntryPoints> = {};
48
+ // $FlowFixMe[incompatible-type]
49
+ const preloadedQueries: Partial<TPreloadedQueries> = {};
50
+ // $FlowFixMe[incompatible-type]
51
+ const preloadedEntryPoints: Partial<TPreloadedEntryPoints> = {};
50
52
  if (queries != null) {
51
53
  const queriesPropNames = Object.keys(queries);
52
54
  queriesPropNames.forEach(queryPropName => {
@@ -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,
@@ -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 ||
@@ -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,
@@ -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
 
@@ -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 ||
@@ -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
@@ -12,17 +12,15 @@
12
12
  'use strict';
13
13
 
14
14
  import type {LoadMoreFn, UseLoadMoreFunctionArgs} from '../useLoadMoreFunction';
15
+ import type {ReturnType} from '../usePaginationFragment';
15
16
  import type {Options} from './useRefetchableFragmentInternal_REACT_CACHE';
16
- import type {RefetchFnDynamic} from './useRefetchableFragmentInternal_REACT_CACHE';
17
17
  import type {
18
18
  FragmentType,
19
19
  GraphQLResponse,
20
- GraphQLTaggedNode,
21
20
  Observer,
22
- OperationType,
21
+ RefetchableFragment,
23
22
  Variables,
24
23
  } from 'relay-runtime';
25
- import type {VariablesOf} from 'relay-runtime/util/RelayRuntimeTypes';
26
24
 
27
25
  const useLoadMoreFunction = require('../useLoadMoreFunction');
28
26
  const useRelayEnvironment = require('../useRelayEnvironment');
@@ -35,36 +33,15 @@ const {
35
33
  getPaginationMetadata,
36
34
  } = require('relay-runtime');
37
35
 
38
- export type ReturnType<TQuery: OperationType, TKey, TFragmentData> = {
39
- data: TFragmentData,
40
- loadNext: LoadMoreFn<TQuery['variables']>,
41
- loadPrevious: LoadMoreFn<TQuery['variables']>,
42
- hasNext: boolean,
43
- hasPrevious: boolean,
44
- isLoadingNext: boolean,
45
- isLoadingPrevious: boolean,
46
- refetch: RefetchFnDynamic<TQuery, TKey>,
47
- };
48
-
49
36
  function usePaginationFragment<
50
- TQuery: OperationType,
51
- TKey: ?{+$data?: mixed, +$fragmentSpreads: FragmentType, ...},
37
+ TFragmentType: FragmentType,
38
+ TVariables: Variables,
39
+ TData,
40
+ TKey: ?{+$fragmentSpreads: TFragmentType, ...},
52
41
  >(
53
- fragmentInput: GraphQLTaggedNode,
42
+ fragmentInput: RefetchableFragment<TFragmentType, TData, TVariables>,
54
43
  parentFragmentRef: TKey,
55
- ): ReturnType<
56
- TQuery,
57
- TKey,
58
- // NOTE: This $Call ensures that the type of the returned data is either:
59
- // - nullable if the provided ref type is nullable
60
- // - non-nullable if the provided ref type is non-nullable
61
- // prettier-ignore
62
- $Call<
63
- & (<TFragmentData>( { +$data?: TFragmentData, ... }) => TFragmentData)
64
- & (<TFragmentData>(?{ +$data?: TFragmentData, ... }) => ?TFragmentData),
65
- TKey,
66
- >,
67
- > {
44
+ ): ReturnType<TVariables, TData, TKey> {
68
45
  const fragmentNode = getFragment(fragmentInput);
69
46
  useStaticFragmentNodeWarning(
70
47
  fragmentNode,
@@ -72,22 +49,18 @@ function usePaginationFragment<
72
49
  );
73
50
  const componentDisplayName = 'usePaginationFragment()';
74
51
 
75
- const {
76
- connectionPathInFragmentData,
77
- paginationRequest,
78
- paginationMetadata,
79
- identifierField,
80
- } = getPaginationMetadata(fragmentNode, componentDisplayName);
52
+ const {connectionPathInFragmentData, paginationRequest, paginationMetadata} =
53
+ getPaginationMetadata(fragmentNode, componentDisplayName);
81
54
 
82
55
  const {fragmentData, fragmentRef, refetch} = useRefetchableFragmentInternal<
83
- TQuery,
84
- TKey,
56
+ {variables: TVariables, response: TData},
57
+ {data?: TData},
85
58
  >(fragmentNode, parentFragmentRef, componentDisplayName);
86
59
  const fragmentIdentifier = getFragmentIdentifier(fragmentNode, fragmentRef);
87
60
 
88
61
  // Backward pagination
89
62
  const [loadPrevious, hasPrevious, isLoadingPrevious, disposeFetchPrevious] =
90
- useLoadMore<TQuery['variables']>({
63
+ useLoadMore<TVariables>({
91
64
  componentDisplayName,
92
65
  connectionPathInFragmentData,
93
66
  direction: 'backward',
@@ -95,29 +68,26 @@ function usePaginationFragment<
95
68
  fragmentIdentifier,
96
69
  fragmentNode,
97
70
  fragmentRef,
98
- identifierField,
99
71
  paginationMetadata,
100
72
  paginationRequest,
101
73
  });
102
74
 
103
75
  // Forward pagination
104
- const [loadNext, hasNext, isLoadingNext, disposeFetchNext] = useLoadMore<
105
- TQuery['variables'],
106
- >({
107
- componentDisplayName,
108
- connectionPathInFragmentData,
109
- direction: 'forward',
110
- fragmentData,
111
- fragmentIdentifier,
112
- fragmentNode,
113
- fragmentRef,
114
- identifierField,
115
- paginationMetadata,
116
- paginationRequest,
117
- });
76
+ const [loadNext, hasNext, isLoadingNext, disposeFetchNext] =
77
+ useLoadMore<TVariables>({
78
+ componentDisplayName,
79
+ connectionPathInFragmentData,
80
+ direction: 'forward',
81
+ fragmentData,
82
+ fragmentIdentifier,
83
+ fragmentNode,
84
+ fragmentRef,
85
+ paginationMetadata,
86
+ paginationRequest,
87
+ });
118
88
 
119
- const refetchPagination: RefetchFnDynamic<TQuery, TKey> = useCallback(
120
- (variables: VariablesOf<TQuery>, options: void | Options) => {
89
+ const refetchPagination = useCallback(
90
+ (variables: TVariables, options: void | Options) => {
121
91
  disposeFetchNext();
122
92
  disposeFetchPrevious();
123
93
  return refetch(variables, {...options, __environment: undefined});
@@ -137,6 +107,7 @@ function usePaginationFragment<
137
107
  });
138
108
  }
139
109
  return {
110
+ // $FlowFixMe[incompatible-return]
140
111
  data: fragmentData,
141
112
  loadNext,
142
113
  loadPrevious,
@@ -11,6 +11,7 @@
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<Partial<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,
@@ -236,6 +237,7 @@ function useRefetchableFragmentNode<
236
237
  debugPreviousIDAndTypename = debugFunctions.getInitialIDAndType(
237
238
  refetchQuery.request.variables,
238
239
  fragmentRefPathInResponse,
240
+ identifierInfo?.identifierQueryVariableName,
239
241
  environment,
240
242
  );
241
243
  }
@@ -343,7 +345,7 @@ function useRefetchableFragmentNode<
343
345
  fragmentIdentifier,
344
346
  fragmentNode,
345
347
  fragmentRefPathInResponse,
346
- identifierField,
348
+ identifierInfo,
347
349
  loadQuery,
348
350
  parentFragmentRef,
349
351
  refetchableRequest,
@@ -377,17 +379,17 @@ function useRefetchFunction<TQuery: OperationType>(
377
379
  fragmentIdentifier: string,
378
380
  fragmentNode: ReaderFragment,
379
381
  fragmentRefPathInResponse: $ReadOnlyArray<string | number>,
380
- identifierField: ?string,
382
+ identifierInfo: ?RefetchableIdentifierInfo,
381
383
  loadQuery: LoaderFn<TQuery>,
382
384
  parentFragmentRef: mixed,
383
385
  refetchableRequest: ConcreteRequest,
384
386
  ): RefetchFn<TQuery, InternalOptions> {
385
387
  const isMountedRef = useIsMountedRef();
386
388
  const identifierValue =
387
- identifierField != null &&
389
+ identifierInfo?.identifierField != null &&
388
390
  fragmentData != null &&
389
391
  typeof fragmentData === 'object'
390
- ? fragmentData[identifierField]
392
+ ? fragmentData[identifierInfo.identifierField]
391
393
  : null;
392
394
  return useCallback(
393
395
  (
@@ -454,8 +456,10 @@ function useRefetchFunction<TQuery: OperationType>(
454
456
  // If the query needs an identifier value ('id' or similar) and one
455
457
  // was not explicitly provided, read it from the fragment data.
456
458
  if (
457
- identifierField != null &&
458
- !providedRefetchVariables.hasOwnProperty('id')
459
+ identifierInfo != null &&
460
+ !providedRefetchVariables.hasOwnProperty(
461
+ identifierInfo.identifierQueryVariableName,
462
+ )
459
463
  ) {
460
464
  // @refetchable fragments are guaranteed to have an `id` selection
461
465
  // if the type is Node, implements Node, or is @fetchable. Double-check
@@ -465,11 +469,13 @@ function useRefetchFunction<TQuery: OperationType>(
465
469
  false,
466
470
  'Relay: Expected result to have a string ' +
467
471
  '`%s` in order to refetch, got `%s`.',
468
- identifierField,
472
+ identifierInfo.identifierField,
469
473
  identifierValue,
470
474
  );
471
475
  }
472
- (refetchVariables: $FlowFixMe).id = identifierValue;
476
+ (refetchVariables: $FlowFixMe)[
477
+ identifierInfo.identifierQueryVariableName
478
+ ] = identifierValue;
473
479
  }
474
480
 
475
481
  const refetchQuery = createOperationDescriptor(
@@ -518,10 +524,11 @@ if (__DEV__) {
518
524
  getInitialIDAndType(
519
525
  memoRefetchVariables: ?Variables,
520
526
  fragmentRefPathInResponse: $ReadOnlyArray<string | number>,
527
+ identifierQueryVariableName: ?string,
521
528
  environment: IEnvironment,
522
529
  ): ?DebugIDandTypename {
523
530
  const {Record} = require('relay-runtime');
524
- const id = memoRefetchVariables?.id;
531
+ const id = memoRefetchVariables?.[identifierQueryVariableName ?? 'id'];
525
532
  if (
526
533
  fragmentRefPathInResponse.length !== 1 ||
527
534
  fragmentRefPathInResponse[0] !== 'node' ||
@@ -531,7 +538,7 @@ if (__DEV__) {
531
538
  }
532
539
  const recordSource = environment.getStore().getSource();
533
540
  const record = recordSource.get(id);
534
- const typename = record && Record.getType(record);
541
+ const typename = record == null ? null : Record.getType(record);
535
542
  if (typename == null) {
536
543
  return null;
537
544
  }