react-relay 15.0.0 → 16.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }