react-relay 15.0.0 → 16.1.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 (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