react-relay 15.0.0 → 16.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) 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 +1 -12
  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/{react-cache/readFragmentInternal_REACT_CACHE.js → experimental/readFragmentInternal_EXPERIMENTAL.js} +5 -29
  48. package/lib/relay-hooks/{react-cache/useFragmentInternal_REACT_CACHE.js → experimental/useFragmentInternal_EXPERIMENTAL.js} +35 -100
  49. package/lib/relay-hooks/{react-cache/useFragment_REACT_CACHE.js → experimental/useFragment_EXPERIMENTAL.js} +1 -16
  50. package/lib/relay-hooks/{react-cache/usePaginationFragment_REACT_CACHE.js → experimental/usePaginationFragment_EXPERIMENTAL.js} +2 -24
  51. package/lib/relay-hooks/{react-cache/useRefetchableFragmentInternal_REACT_CACHE.js → experimental/useRefetchableFragmentInternal_EXPERIMENTAL.js} +14 -98
  52. package/lib/relay-hooks/{react-cache/useRefetchableFragment_REACT_CACHE.js → experimental/useRefetchableFragment_EXPERIMENTAL.js} +1 -15
  53. package/lib/relay-hooks/loadEntryPoint.js +1 -24
  54. package/lib/relay-hooks/loadQuery.js +2 -106
  55. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +2 -27
  56. package/lib/relay-hooks/prepareEntryPoint_DEPRECATED.js +0 -13
  57. package/lib/relay-hooks/useBlockingPaginationFragment.js +0 -42
  58. package/lib/relay-hooks/useClientQuery.js +0 -18
  59. package/lib/relay-hooks/useEntryPointLoader.js +0 -69
  60. package/lib/relay-hooks/useFetchTrackingRef.js +0 -26
  61. package/lib/relay-hooks/useFragment.js +0 -17
  62. package/lib/relay-hooks/useFragmentNode.js +2 -32
  63. package/lib/relay-hooks/useIsMountedRef.js +0 -11
  64. package/lib/relay-hooks/useIsOperationNodeActive.js +0 -11
  65. package/lib/relay-hooks/useIsParentQueryActive.js +0 -11
  66. package/lib/relay-hooks/useLazyLoadQuery.js +0 -18
  67. package/lib/relay-hooks/useLazyLoadQueryNode.js +12 -37
  68. package/lib/relay-hooks/useLoadMoreFunction.js +9 -34
  69. package/lib/relay-hooks/useMemoOperationDescriptor.js +0 -11
  70. package/lib/relay-hooks/useMemoVariables.js +0 -17
  71. package/lib/relay-hooks/useMutation.js +0 -11
  72. package/lib/relay-hooks/usePaginationFragment.js +1 -26
  73. package/lib/relay-hooks/usePreloadedQuery.js +0 -27
  74. package/lib/relay-hooks/useQueryLoader.js +0 -74
  75. package/lib/relay-hooks/useRefetchableFragment.js +0 -16
  76. package/lib/relay-hooks/useRefetchableFragmentNode.js +14 -97
  77. package/lib/relay-hooks/useRelayEnvironment.js +0 -11
  78. package/lib/relay-hooks/useStaticFragmentNodeWarning.js +0 -15
  79. package/lib/relay-hooks/useSubscribeToInvalidationState.js +0 -25
  80. package/lib/relay-hooks/useSubscription.js +0 -15
  81. package/lib/relay-hooks/useUnsafeRef_DEPRECATED.js +0 -17
  82. package/package.json +2 -2
  83. package/react-relay-hooks.js +2 -2
  84. package/react-relay-hooks.min.js +2 -2
  85. package/react-relay-legacy.js +2 -2
  86. package/react-relay-legacy.min.js +2 -2
  87. package/react-relay.js +2 -2
  88. package/react-relay.min.js +2 -2
  89. package/relay-hooks/EntryPointContainer.react.js.flow +5 -0
  90. package/relay-hooks/EntryPointTypes.flow.js.flow +34 -35
  91. package/relay-hooks/FragmentResource.js.flow +114 -26
  92. package/relay-hooks/HooksImplementation.js.flow +3 -1
  93. package/relay-hooks/LazyLoadEntryPointContainer_DEPRECATED.react.js.flow +4 -2
  94. package/relay-hooks/NestedRelayEntryPointBuilderUtils.js.flow +51 -0
  95. package/relay-hooks/__flowtests__/EntryPointTypes/NestedEntrypoints-flowtest.js.flow +7 -5
  96. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +5 -0
  97. package/relay-hooks/__flowtests__/usePaginationFragment-flowtest.js.flow +5 -0
  98. package/relay-hooks/__flowtests__/useRefetchableFragment-flowtest.js.flow +2 -0
  99. package/relay-hooks/{react-cache/readFragmentInternal_REACT_CACHE.js.flow → experimental/readFragmentInternal_EXPERIMENTAL.js.flow} +4 -3
  100. package/relay-hooks/{react-cache/useFragmentInternal_REACT_CACHE.js.flow → experimental/useFragmentInternal_EXPERIMENTAL.js.flow} +32 -14
  101. package/relay-hooks/{react-cache/useFragment_REACT_CACHE.js.flow → experimental/useFragment_EXPERIMENTAL.js.flow} +4 -10
  102. package/relay-hooks/{react-cache/usePaginationFragment_REACT_CACHE.js.flow → experimental/usePaginationFragment_EXPERIMENTAL.js.flow} +30 -59
  103. package/relay-hooks/{react-cache/useRefetchableFragmentInternal_REACT_CACHE.js.flow → experimental/useRefetchableFragmentInternal_EXPERIMENTAL.js.flow} +30 -23
  104. package/relay-hooks/experimental/useRefetchableFragment_EXPERIMENTAL.js.flow +49 -0
  105. package/relay-hooks/loadEntryPoint.js.flow +4 -2
  106. package/relay-hooks/loadQuery.js.flow +21 -1
  107. package/relay-hooks/prepareEntryPoint_DEPRECATED.js.flow +4 -2
  108. package/relay-hooks/useBlockingPaginationFragment.js.flow +10 -17
  109. package/relay-hooks/useClientQuery.js.flow +2 -2
  110. package/relay-hooks/useFragmentNode.js.flow +2 -2
  111. package/relay-hooks/useLazyLoadQueryNode.js.flow +17 -1
  112. package/relay-hooks/useLoadMoreFunction.js.flow +15 -9
  113. package/relay-hooks/useMutation.js.flow +26 -9
  114. package/relay-hooks/usePaginationFragment.js.flow +7 -15
  115. package/relay-hooks/useQueryLoader.js.flow +2 -8
  116. package/relay-hooks/useRefetchableFragment.js.flow +14 -16
  117. package/relay-hooks/useRefetchableFragmentNode.js.flow +33 -20
  118. package/lib/relay-hooks/react-cache/RelayReactCache.js +0 -32
  119. package/lib/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js +0 -290
  120. package/lib/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js +0 -49
  121. package/lib/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js +0 -110
  122. package/relay-hooks/react-cache/RelayReactCache.js.flow +0 -40
  123. package/relay-hooks/react-cache/getQueryResultOrFetchQuery_REACT_CACHE.js.flow +0 -430
  124. package/relay-hooks/react-cache/useLazyLoadQuery_REACT_CACHE.js.flow +0 -70
  125. package/relay-hooks/react-cache/usePreloadedQuery_REACT_CACHE.js.flow +0 -150
  126. package/relay-hooks/react-cache/useRefetchableFragment_REACT_CACHE.js.flow +0 -65
@@ -50,7 +50,12 @@ type FragmentResourceCache = Cache<
50
50
  promise: Promise<mixed>,
51
51
  result: FragmentResult,
52
52
  }
53
- | {kind: 'done', result: FragmentResult},
53
+ | {kind: 'done', result: FragmentResult}
54
+ | {
55
+ kind: 'missing',
56
+ result: FragmentResult,
57
+ snapshot: SingularOrPluralSnapshot,
58
+ },
54
59
  >;
55
60
 
56
61
  const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
@@ -348,16 +353,28 @@ class FragmentResourceImpl {
348
353
  componentDisplayName,
349
354
  );
350
355
 
351
- const snapshot =
352
- fragmentSelector.kind === 'PluralReaderSelector'
353
- ? fragmentSelector.selectors.map(s => environment.lookup(s))
354
- : environment.lookup(fragmentSelector);
356
+ let fragmentResult = null;
357
+ let snapshot = null;
358
+ // Fall through to existing logic if it's 'missing' state so it would check and save promise into cache.
359
+ if (
360
+ RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
361
+ cachedValue != null &&
362
+ cachedValue.kind === 'missing'
363
+ ) {
364
+ fragmentResult = cachedValue.result;
365
+ snapshot = cachedValue.snapshot;
366
+ } else {
367
+ snapshot =
368
+ fragmentSelector.kind === 'PluralReaderSelector'
369
+ ? fragmentSelector.selectors.map(s => environment.lookup(s))
370
+ : environment.lookup(fragmentSelector);
355
371
 
356
- const fragmentResult = getFragmentResult(
357
- fragmentIdentifier,
358
- snapshot,
359
- storeEpoch,
360
- );
372
+ fragmentResult = getFragmentResult(
373
+ fragmentIdentifier,
374
+ snapshot,
375
+ storeEpoch,
376
+ );
377
+ }
361
378
  if (!fragmentResult.isMissingData) {
362
379
  this._throwOrLogErrorsInSnapshot(snapshot);
363
380
 
@@ -483,6 +500,17 @@ class FragmentResourceImpl {
483
500
  }
484
501
  }
485
502
 
503
+ // set it as done if has missing data and no pending operations
504
+ if (
505
+ RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
506
+ fragmentResult.isMissingData
507
+ ) {
508
+ this._cache.set(fragmentIdentifier, {
509
+ kind: 'done',
510
+ result: fragmentResult,
511
+ });
512
+ }
513
+
486
514
  this._throwOrLogErrorsInSnapshot(snapshot);
487
515
 
488
516
  // At this point, there's nothing we can do. We don't have all the expected
@@ -617,10 +645,26 @@ class FragmentResourceImpl {
617
645
  disposables.push(
618
646
  environment.subscribe(currentSnapshot, latestSnapshot => {
619
647
  const storeEpoch = environment.getStore().getEpoch();
620
- this._cache.set(cacheKey, {
621
- kind: 'done',
622
- result: getFragmentResult(cacheKey, latestSnapshot, storeEpoch),
623
- });
648
+ const result = getFragmentResult(
649
+ cacheKey,
650
+ latestSnapshot,
651
+ storeEpoch,
652
+ );
653
+ if (
654
+ RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
655
+ result.isMissingData
656
+ ) {
657
+ this._cache.set(cacheKey, {
658
+ kind: 'missing',
659
+ result: result,
660
+ snapshot: latestSnapshot,
661
+ });
662
+ } else {
663
+ this._cache.set(cacheKey, {
664
+ kind: 'done',
665
+ result: getFragmentResult(cacheKey, latestSnapshot, storeEpoch),
666
+ });
667
+ }
624
668
  callback();
625
669
  }),
626
670
  );
@@ -696,10 +740,26 @@ class FragmentResourceImpl {
696
740
  // Only update the cache when the data is changed to avoid
697
741
  // returning different `data` instances
698
742
  if (didMissUpdates) {
699
- this._cache.set(cacheKey, {
700
- kind: 'done',
701
- result: getFragmentResult(cacheKey, currentSnapshots, storeEpoch),
702
- });
743
+ const result = getFragmentResult(
744
+ cacheKey,
745
+ currentSnapshots,
746
+ storeEpoch,
747
+ );
748
+ if (
749
+ RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
750
+ result.isMissingData
751
+ ) {
752
+ this._cache.set(cacheKey, {
753
+ kind: 'missing',
754
+ result,
755
+ snapshot: currentSnapshots,
756
+ });
757
+ } else {
758
+ this._cache.set(cacheKey, {
759
+ kind: 'done',
760
+ result,
761
+ });
762
+ }
703
763
  }
704
764
  return [didMissUpdates, currentSnapshots];
705
765
  }
@@ -718,10 +778,26 @@ class FragmentResourceImpl {
718
778
  relayResolverErrors: currentSnapshot.relayResolverErrors,
719
779
  };
720
780
  if (updatedData !== renderData) {
721
- this._cache.set(cacheKey, {
722
- kind: 'done',
723
- result: getFragmentResult(cacheKey, updatedCurrentSnapshot, storeEpoch),
724
- });
781
+ const result = getFragmentResult(
782
+ cacheKey,
783
+ updatedCurrentSnapshot,
784
+ storeEpoch,
785
+ );
786
+ if (
787
+ RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
788
+ result.isMissingData
789
+ ) {
790
+ this._cache.set(cacheKey, {
791
+ kind: 'missing',
792
+ result: result,
793
+ snapshot: updatedCurrentSnapshot,
794
+ });
795
+ } else {
796
+ this._cache.set(cacheKey, {
797
+ kind: 'done',
798
+ result,
799
+ });
800
+ }
725
801
  }
726
802
  return [updatedData !== renderData, updatedCurrentSnapshot];
727
803
  }
@@ -799,10 +875,22 @@ class FragmentResourceImpl {
799
875
  ? [...currentSnapshot]
800
876
  : [...baseSnapshots];
801
877
  nextSnapshots[idx] = latestSnapshot;
802
- this._cache.set(cacheKey, {
803
- kind: 'done',
804
- result: getFragmentResult(cacheKey, nextSnapshots, storeEpoch),
805
- });
878
+ const result = getFragmentResult(cacheKey, nextSnapshots, storeEpoch);
879
+ if (
880
+ RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE &&
881
+ result.isMissingData
882
+ ) {
883
+ this._cache.set(cacheKey, {
884
+ kind: 'missing',
885
+ result,
886
+ snapshot: nextSnapshots,
887
+ });
888
+ } else {
889
+ this._cache.set(cacheKey, {
890
+ kind: 'done',
891
+ result,
892
+ });
893
+ }
806
894
  }
807
895
  }
808
896
 
@@ -11,6 +11,7 @@
11
11
 
12
12
  'use strict';
13
13
 
14
+ import typeof useFragmentInternal from './experimental/useFragmentInternal_EXPERIMENTAL';
14
15
  import typeof useFragment from './useFragment';
15
16
  import type {UsePaginationFragmentType} from './usePaginationFragment';
16
17
  import type {UseRefetchableFragmentType} from './useRefetchableFragment';
@@ -21,13 +22,14 @@ type HooksImplementation = {
21
22
  useFragment: useFragment,
22
23
  usePaginationFragment: UsePaginationFragmentType,
23
24
  useRefetchableFragment: UseRefetchableFragmentType,
25
+ useFragment__internal?: useFragmentInternal,
24
26
  };
25
27
 
26
28
  let implementation: HooksImplementation | null = null;
27
29
 
28
30
  function inject(impl: HooksImplementation): void {
29
31
  warning(
30
- implementation !== null,
32
+ implementation === null,
31
33
  'Relay HooksImplementation was injected twice.',
32
34
  );
33
35
  implementation = impl;
@@ -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>,
@@ -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,
@@ -156,7 +157,7 @@ function getFragmentState(
156
157
  }
157
158
 
158
159
  // fragmentNode cannot change during the lifetime of the component, though fragmentRef may change.
159
- function readFragmentInternal_REACT_CACHE(
160
+ function readFragmentInternal_EXPERIMENTAL(
160
161
  environment: IEnvironment,
161
162
  fragmentNode: ReaderFragment,
162
163
  fragmentRef: mixed,
@@ -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 ||
@@ -294,4 +295,4 @@ function readFragmentInternal_REACT_CACHE(
294
295
  return {data, clientEdgeQueries};
295
296
  }
296
297
 
297
- module.exports = readFragmentInternal_REACT_CACHE;
298
+ module.exports = readFragmentInternal_EXPERIMENTAL;
@@ -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;
@@ -323,12 +344,11 @@ function getFragmentState(
323
344
  }
324
345
 
325
346
  // fragmentNode cannot change during the lifetime of the component, though fragmentRef may change.
326
- function useFragmentInternal_REACT_CACHE(
347
+ function useFragmentInternal_EXPERIMENTAL(
327
348
  fragmentNode: ReaderFragment,
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
 
@@ -418,7 +435,7 @@ function useFragmentInternal_REACT_CACHE(
418
435
  // The purpose of this is to detect whether we have ever committed, because we
419
436
  // don't suspend on store updates, only when the component either is first trying
420
437
  // to mount or when the our selector changes. The selector change in particular is
421
- // how we suspend for pagination and refetech. Also, fragment selector can be null
438
+ // how we suspend for pagination and refetch. Also, fragment selector can be null
422
439
  // or undefined, so we use false as a special value to distinguish from all fragment
423
440
  // selectors; false means that the component hasn't mounted yet.
424
441
  const committedFragmentSelectorRef = useRef<false | ?ReaderSelector>(false);
@@ -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;
@@ -533,7 +551,7 @@ function useFragmentInternal_REACT_CACHE(
533
551
 
534
552
  let data: ?SelectorData | Array<?SelectorData>;
535
553
  if (isPlural) {
536
- // Plural fragments require allocating an array of the snasphot data values,
554
+ // Plural fragments require allocating an array of the snapshot data values,
537
555
  // which has to be memoized to avoid triggering downstream re-renders.
538
556
  //
539
557
  // Note that isPlural is a constant property of the fragment and does not change
@@ -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 ||
@@ -596,4 +614,4 @@ function useFragmentInternal_REACT_CACHE(
596
614
  return data;
597
615
  }
598
616
 
599
- module.exports = useFragmentInternal_REACT_CACHE;
617
+ module.exports = useFragmentInternal_EXPERIMENTAL;
@@ -15,7 +15,7 @@ import type {Fragment, FragmentType, GraphQLTaggedNode} from 'relay-runtime';
15
15
 
16
16
  const {useTrackLoadQueryInRender} = require('../loadQuery');
17
17
  const useStaticFragmentNodeWarning = require('../useStaticFragmentNodeWarning');
18
- const useFragmentInternal = require('./useFragmentInternal_REACT_CACHE');
18
+ const useFragmentInternal = require('./useFragmentInternal_EXPERIMENTAL');
19
19
  const {useDebugValue} = require('react');
20
20
  const {getFragment} = require('relay-runtime');
21
21
 
@@ -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