react-relay 18.1.0 → 19.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 (67) hide show
  1. package/ReactRelayContainerUtils.js.flow +2 -2
  2. package/ReactRelayContext.js +1 -1
  3. package/ReactRelayFragmentContainer.js.flow +5 -10
  4. package/ReactRelayLoggingContext.js.flow +21 -0
  5. package/ReactRelayPaginationContainer.js.flow +8 -8
  6. package/ReactRelayRefetchContainer.js.flow +8 -8
  7. package/ReactRelayTypes.js.flow +18 -11
  8. package/__flowtests__/ReactRelayFragmentContainer-flowtest.js.flow +1 -8
  9. package/__flowtests__/ReactRelayPaginationContainer-flowtest.js.flow +2 -5
  10. package/__flowtests__/ReactRelayRefetchContainer-flowtest.js.flow +2 -5
  11. package/buildReactRelayContainer.js.flow +3 -3
  12. package/hooks.js +1 -1
  13. package/index.js +1 -1
  14. package/index.js.flow +3 -0
  15. package/legacy.js +1 -1
  16. package/lib/ReactRelayLoggingContext.js +6 -0
  17. package/lib/index.js +2 -0
  18. package/lib/relay-hooks/legacy/FragmentResource.js +4 -5
  19. package/lib/relay-hooks/legacy/useRefetchableFragmentNode.js +1 -1
  20. package/lib/relay-hooks/loadEntryPoint.js +3 -0
  21. package/lib/relay-hooks/loadQuery.js +9 -16
  22. package/lib/relay-hooks/preloadQuery_DEPRECATED.js +7 -2
  23. package/lib/relay-hooks/readFragmentInternal.js +2 -2
  24. package/lib/relay-hooks/useFragmentInternal_CURRENT.js +12 -8
  25. package/lib/relay-hooks/useFragmentInternal_EXPERIMENTAL.js +12 -8
  26. package/lib/relay-hooks/useLoadMoreFunction.js +3 -2
  27. package/lib/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js +3 -2
  28. package/lib/relay-hooks/usePaginationFragment.js +5 -1
  29. package/lib/relay-hooks/usePrefetchableForwardPaginationFragment.js +228 -0
  30. package/lib/relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL.js +227 -0
  31. package/lib/relay-hooks/useRefetchableFragmentInternal.js +3 -3
  32. package/lib/relay-hooks/useRelayLoggingContext.js +9 -0
  33. package/package.json +3 -3
  34. package/relay-hooks/EntryPointContainer.react.js.flow +13 -17
  35. package/relay-hooks/EntryPointTypes.flow.js.flow +7 -5
  36. package/relay-hooks/MatchContainer.js.flow +1 -1
  37. package/relay-hooks/legacy/FragmentResource.js.flow +5 -9
  38. package/relay-hooks/legacy/useBlockingPaginationFragment.js.flow +2 -17
  39. package/relay-hooks/legacy/useRefetchableFragmentNode.js.flow +1 -1
  40. package/relay-hooks/loadEntryPoint.js.flow +6 -0
  41. package/relay-hooks/loadQuery.js.flow +47 -39
  42. package/relay-hooks/preloadQuery_DEPRECATED.js.flow +18 -3
  43. package/relay-hooks/readFragmentInternal.js.flow +2 -5
  44. package/relay-hooks/useEntryPointLoader.js.flow +4 -2
  45. package/relay-hooks/useFragment.js.flow +1 -0
  46. package/relay-hooks/useFragmentInternal.js.flow +2 -0
  47. package/relay-hooks/useFragmentInternal_CURRENT.js.flow +27 -12
  48. package/relay-hooks/useFragmentInternal_EXPERIMENTAL.js.flow +29 -12
  49. package/relay-hooks/useLazyLoadQuery.js.flow +4 -4
  50. package/relay-hooks/useLoadMoreFunction.js.flow +7 -7
  51. package/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js.flow +5 -7
  52. package/relay-hooks/usePaginationFragment.js.flow +6 -10
  53. package/relay-hooks/usePrefetchableForwardPaginationFragment.js.flow +436 -0
  54. package/relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL.js.flow +435 -0
  55. package/relay-hooks/usePreloadedQuery.js.flow +1 -0
  56. package/relay-hooks/useQueryLoader.js.flow +5 -1
  57. package/relay-hooks/useQueryLoader_EXPERIMENTAL.js.flow +3 -1
  58. package/relay-hooks/useRefetchableFragment.js.flow +1 -0
  59. package/relay-hooks/useRefetchableFragmentInternal.js.flow +3 -3
  60. package/relay-hooks/useRelayLoggingContext.js.flow +21 -0
  61. package/relay-hooks/useStaticFragmentNodeWarning.js.flow +1 -0
  62. package/react-relay-hooks.js +0 -4
  63. package/react-relay-hooks.min.js +0 -9
  64. package/react-relay-legacy.js +0 -4
  65. package/react-relay-legacy.min.js +0 -9
  66. package/react-relay.js +0 -4
  67. package/react-relay.min.js +0 -9
@@ -22,29 +22,25 @@ const React = require('react');
22
22
  const {useContext, useEffect} = require('react');
23
23
  const warning = require('warning');
24
24
 
25
- function EntryPointContainer<
25
+ component EntryPointContainer<
26
26
  // $FlowFixMe[unsupported-variance-annotation]
27
- +TPreloadedQueries: {...},
27
+ TRuntimeProps: {...},
28
+ TRenders: React.Node,
28
29
  // $FlowFixMe[unsupported-variance-annotation]
29
- +TPreloadedNestedEntryPoints: {...},
30
- // $FlowFixMe[unsupported-variance-annotation]
31
- +TRuntimeProps: {...},
32
- // $FlowFixMe[unsupported-variance-annotation]
33
- +TExtraProps,
34
- // $FlowFixMe[unsupported-variance-annotation]
35
- +TEntryPointComponent: EntryPointComponent<
36
- TPreloadedQueries,
37
- TPreloadedNestedEntryPoints,
30
+ TEntryPointComponent: EntryPointComponent<
31
+ // $FlowExpectedErrors[unclear-type] Use any to accept all kinds of EntryPointComponent
32
+ any,
33
+ // $FlowExpectedErrors[unclear-type] Use any to accept all kinds of EntryPointComponent
34
+ any,
38
35
  TRuntimeProps,
39
- TExtraProps,
36
+ // $FlowExpectedErrors[unclear-type] Use any to accept all kinds of EntryPointComponent
37
+ any,
38
+ TRenders,
40
39
  >,
41
- >({
42
- entryPointReference,
43
- props,
44
- }: $ReadOnly<{
40
+ >(
45
41
  entryPointReference: PreloadedEntryPoint<TEntryPointComponent>,
46
42
  props: TRuntimeProps,
47
- }>): React.MixedElement {
43
+ ) renders TRenders {
48
44
  warning(
49
45
  entryPointReference.isDisposed === false,
50
46
  '<EntryPointContainer>: Expected entryPointReference to not be disposed ' +
@@ -14,7 +14,7 @@
14
14
  /* eslint-disable no-unused-vars */
15
15
 
16
16
  import type {JSResourceReference} from 'JSResourceReference';
17
- import type {AbstractComponent, ElementConfig} from 'react';
17
+ import type {ComponentType, ElementConfig} from 'react';
18
18
  import type {
19
19
  CacheConfig,
20
20
  FetchPolicy,
@@ -36,6 +36,7 @@ export type PreloadFetchPolicy =
36
36
  export type PreloadOptions = {
37
37
  +fetchKey?: string | number,
38
38
  +fetchPolicy?: ?PreloadFetchPolicy,
39
+ +includeIf?: ?boolean,
39
40
  +networkCacheConfig?: ?CacheConfig,
40
41
  };
41
42
 
@@ -178,14 +179,15 @@ export type EntryPointComponent<
178
179
  TPreloadedEntryPoints = {},
179
180
  TRuntimeProps = {},
180
181
  TExtraProps = null,
181
- > = AbstractComponent<
182
- EntryPointProps<
182
+ TRenders: React.Node = React.Node,
183
+ > = component(
184
+ ...EntryPointProps<
183
185
  TPreloadedQueries,
184
186
  TPreloadedEntryPoints,
185
187
  TRuntimeProps,
186
188
  TExtraProps,
187
- >,
188
- >;
189
+ >
190
+ ) renders TRenders;
189
191
 
190
192
  // Return type of the `getPreloadProps(...)` of the entry point
191
193
  export type PreloadProps<
@@ -95,7 +95,7 @@ export type MatchPointer = {
95
95
 
96
96
  export type MatchContainerProps<TProps: {...}, TFallback: React.Node> = {
97
97
  +fallback?: ?TFallback,
98
- +loader: (module: mixed) => React.AbstractComponent<TProps>,
98
+ +loader: (module: mixed) => component(...TProps),
99
99
  +match: ?MatchPointer | ?TypenameOnlyPointer,
100
100
  +props?: TProps,
101
101
  };
@@ -22,7 +22,6 @@ import type {
22
22
  RequestDescriptor,
23
23
  Snapshot,
24
24
  } from 'relay-runtime';
25
- import type {MissingLiveResolverField} from 'relay-runtime/store/RelayStoreTypes';
26
25
 
27
26
  const LRUCache = require('../LRUCache');
28
27
  const {getQueryResourceForEnvironment} = require('../QueryResource');
@@ -98,7 +97,7 @@ function hasMissingClientEdges(snapshot: SingularOrPluralSnapshot): boolean {
98
97
 
99
98
  function missingLiveResolverFields(
100
99
  snapshot: SingularOrPluralSnapshot,
101
- ): ?$ReadOnlyArray<MissingLiveResolverField> {
100
+ ): ?$ReadOnlyArray<DataID> {
102
101
  if (Array.isArray(snapshot)) {
103
102
  return snapshot
104
103
  .map(s => s.missingLiveResolverFields)
@@ -449,7 +448,7 @@ class FragmentResourceImpl {
449
448
  );
450
449
  const parentQueryPromiseResultPromise = parentQueryPromiseResult?.promise; // for refinement
451
450
  const missingResolverFieldPromises =
452
- missingLiveResolverFields(snapshot)?.map(({liveStateID}) => {
451
+ missingLiveResolverFields(snapshot)?.map(liveStateID => {
453
452
  const store = environment.getStore();
454
453
 
455
454
  // $FlowFixMe[prop-missing] This is expected to be a RelayModernStore
@@ -559,13 +558,10 @@ class FragmentResourceImpl {
559
558
  _throwOrLogErrorsInSnapshot(snapshot: SingularOrPluralSnapshot) {
560
559
  if (Array.isArray(snapshot)) {
561
560
  snapshot.forEach(s => {
562
- handlePotentialSnapshotErrors(this._environment, s.errorResponseFields);
561
+ handlePotentialSnapshotErrors(this._environment, s.fieldErrors);
563
562
  });
564
563
  } else {
565
- handlePotentialSnapshotErrors(
566
- this._environment,
567
- snapshot.errorResponseFields,
568
- );
564
+ handlePotentialSnapshotErrors(this._environment, snapshot.fieldErrors);
569
565
  }
570
566
  }
571
567
 
@@ -764,7 +760,7 @@ class FragmentResourceImpl {
764
760
  missingLiveResolverFields: currentSnapshot.missingLiveResolverFields,
765
761
  seenRecords: currentSnapshot.seenRecords,
766
762
  selector: currentSnapshot.selector,
767
- errorResponseFields: currentSnapshot.errorResponseFields,
763
+ fieldErrors: currentSnapshot.fieldErrors,
768
764
  };
769
765
  if (updatedData !== renderData) {
770
766
  const result = getFragmentResult(
@@ -14,13 +14,7 @@
14
14
  import type {LoadMoreFn, UseLoadMoreFunctionArgs} from '../useLoadMoreFunction';
15
15
  import type {Options} from './useRefetchableFragmentNode';
16
16
  import type {RefetchableFragment} from 'relay-runtime';
17
- import type {
18
- Disposable,
19
- FragmentType,
20
- GraphQLResponse,
21
- Observer,
22
- Variables,
23
- } from 'relay-runtime';
17
+ import type {Disposable, FragmentType, Variables} from 'relay-runtime';
24
18
 
25
19
  const useLoadMoreFunction = require('../useLoadMoreFunction');
26
20
  const useStaticFragmentNodeWarning = require('../useStaticFragmentNodeWarning');
@@ -169,16 +163,7 @@ hook useBlockingPaginationFragment<
169
163
  hook useLoadMore<TVariables: Variables>(args: {
170
164
  disableStoreUpdates: () => void,
171
165
  enableStoreUpdates: () => void,
172
- ...$Exact<
173
- $Diff<
174
- UseLoadMoreFunctionArgs,
175
- {
176
- observer: Observer<GraphQLResponse>,
177
- onReset: () => void,
178
- ...
179
- },
180
- >,
181
- >,
166
+ ...$Exact<Omit<UseLoadMoreFunctionArgs, 'observer' | 'onReset'>>,
182
167
  }): [LoadMoreFn<TVariables>, boolean, () => void] {
183
168
  const {disableStoreUpdates, enableStoreUpdates, ...loadMoreArgs} = args;
184
169
  const [requestPromise, setRequestPromise] = useState<null | Promise<mixed>>(
@@ -592,7 +592,7 @@ if (__DEV__) {
592
592
  fragmentNode: ReaderFragment,
593
593
  componentDisplayName: string,
594
594
  ): void {
595
- if (previousIDAndTypename == null) {
595
+ if (previousIDAndTypename == null || refetchedFragmentRef == null) {
596
596
  return;
597
597
  }
598
598
  const {ID_KEY} = require('relay-runtime');
@@ -62,6 +62,12 @@ function loadEntryPoint<
62
62
  const {environmentProviderOptions, options, parameters, variables} =
63
63
  query;
64
64
 
65
+ // $FlowFixMe[prop-missing] Exists for types that wrap EntryPoint
66
+ if (options?.includeIf === false) {
67
+ // don't preload this query since the includeIf is false
68
+ return;
69
+ }
70
+
65
71
  const environment = environmentProvider.getEnvironment(
66
72
  environmentProviderOptions,
67
73
  );
@@ -28,13 +28,13 @@ import type {
28
28
  RequestIdentifier,
29
29
  RequestParameters,
30
30
  } from 'relay-runtime';
31
+ import type {OperationAvailability} from 'relay-runtime/store/RelayStoreTypes';
31
32
 
32
33
  const invariant = require('invariant');
33
34
  const {
34
35
  __internal: {fetchQueryDeduped},
35
36
  Observable,
36
37
  PreloadableQueryRegistry,
37
- RelayFeatureFlags,
38
38
  ReplaySubject,
39
39
  createOperationDescriptor,
40
40
  getRequest,
@@ -131,6 +131,7 @@ function loadQuery<
131
131
  let didMakeNetworkRequest = false;
132
132
  const makeNetworkRequest = (
133
133
  params: RequestParameters,
134
+ checkOperation?: () => OperationAvailability,
134
135
  ): Observable<GraphQLResponse> => {
135
136
  // N.B. this function is called synchronously or not at all
136
137
  // didMakeNetworkRequest is safe to rely on in the returned value
@@ -140,34 +141,38 @@ function loadQuery<
140
141
  // `source` observable is returned.
141
142
  didMakeNetworkRequest = true;
142
143
 
143
- let observable;
144
144
  const subject = new ReplaySubject<GraphQLResponse>();
145
- if (RelayFeatureFlags.ENABLE_LOAD_QUERY_REQUEST_DEDUPING === true) {
146
- // Here, we are calling fetchQueryDeduped at the network layer level,
147
- // which ensures that only a single network request is active for a given
148
- // (environment, identifier) pair.
149
- // Since network requests can be started /before/ we have the query ast
150
- // necessary to process the results, we need to dedupe the raw requests
151
- // separately from deduping the operation execution; specifically,
152
- // if `loadQuery` is called multiple times before the query ast is available,
153
- // we still want the network request to be deduped.
154
- // - If a duplicate active network request is found, it will return an
155
- // Observable that replays the events of the already active request.
156
- // - If no duplicate active network request is found, it will call the fetchFn
157
- // to start the request, and return an Observable that will replay
158
- // the events from the network request.
159
- // We provide an extra key to the identifier to distinguish deduping
160
- // of raw network requests vs deduping of operation executions.
161
- const identifier: RequestIdentifier =
162
- 'raw-network-request-' + getRequestIdentifier(params, variables);
163
- observable = fetchQueryDeduped(environment, identifier, () => {
164
- const network = environment.getNetwork();
165
- return network.execute(params, variables, networkCacheConfig);
166
- });
167
- } else {
145
+
146
+ // Here, we are calling fetchQueryDeduped at the network layer level,
147
+ // which ensures that only a single network request is active for a given
148
+ // (environment, identifier) pair.
149
+ // Since network requests can be started /before/ we have the query ast
150
+ // necessary to process the results, we need to dedupe the raw requests
151
+ // separately from deduping the operation execution; specifically,
152
+ // if `loadQuery` is called multiple times before the query ast is available,
153
+ // we still want the network request to be deduped.
154
+ // - If a duplicate active network request is found, it will return an
155
+ // Observable that replays the events of the already active request.
156
+ // - If no duplicate active network request is found, it will call the fetchFn
157
+ // to start the request, and return an Observable that will replay
158
+ // the events from the network request.
159
+ // We provide an extra key to the identifier to distinguish deduping
160
+ // of raw network requests vs deduping of operation executions.
161
+ const identifier: RequestIdentifier =
162
+ 'raw-network-request-' + getRequestIdentifier(params, variables);
163
+ const observable = fetchQueryDeduped(environment, identifier, () => {
168
164
  const network = environment.getNetwork();
169
- observable = network.execute(params, variables, networkCacheConfig);
170
- }
165
+ return network.execute(
166
+ params,
167
+ variables,
168
+ networkCacheConfig,
169
+ undefined,
170
+ undefined,
171
+ undefined,
172
+ undefined,
173
+ checkOperation,
174
+ );
175
+ });
171
176
 
172
177
  const {unsubscribe} = observable.subscribe({
173
178
  error(err) {
@@ -196,17 +201,15 @@ function loadQuery<
196
201
  operation: OperationDescriptor,
197
202
  fetchFn: () => Observable<GraphQLResponse>,
198
203
  ) => {
199
- if (RelayFeatureFlags.ENABLE_LOAD_QUERY_REQUEST_DEDUPING === true) {
200
- // N.B. at this point, if we're calling execute with a query ast (OperationDescriptor),
201
- // we are guaranteed to have started a network request. We set this to
202
- // true here as well since `makeNetworkRequest` might get skipped in the case
203
- // where the query ast is already available and the query executions get deduped.
204
- // Even if the execution gets deduped below, we still wan't to return
205
- // an observable that provides the replayed network events for the query,
206
- // so we set this to true before deduping, to guarantee that the `source`
207
- // observable is returned.
208
- didMakeNetworkRequest = true;
209
- }
204
+ // N.B. at this point, if we're calling execute with a query ast (OperationDescriptor),
205
+ // we are guaranteed to have started a network request. We set this to
206
+ // true here as well since `makeNetworkRequest` might get skipped in the case
207
+ // where the query ast is already available and the query executions get deduped.
208
+ // Even if the execution gets deduped below, we still wan't to return
209
+ // an observable that provides the replayed network events for the query,
210
+ // so we set this to true before deduping, to guarantee that the `source`
211
+ // observable is returned.
212
+ didMakeNetworkRequest = true;
210
213
 
211
214
  // Here, we are calling fetchQueryDeduped, which ensures that only
212
215
  // a single operation is active for a given (environment, identifier) pair,
@@ -255,13 +258,18 @@ function loadQuery<
255
258
  // then we do nothing.
256
259
  const shouldFetch =
257
260
  fetchPolicy !== 'store-or-network' ||
261
+ // environment.check can trigger store updates through missing field handlers,
262
+ // short circuiting the check avoids unnecessary updates
258
263
  environment.check(operation).status !== 'available';
259
264
 
260
265
  if (shouldFetch) {
261
266
  executeDeduped(operation, () => {
262
267
  // N.B. Since we have the operation synchronously available here,
263
268
  // we can immediately fetch and execute the operation.
264
- const networkObservable = makeNetworkRequest(concreteRequest.params);
269
+ const networkObservable = makeNetworkRequest(
270
+ concreteRequest.params,
271
+ () => environment.check(operation),
272
+ );
265
273
  const executeObservable = executeWithNetworkSource(
266
274
  operation,
267
275
  networkObservable,
@@ -24,6 +24,7 @@ import type {
24
24
  GraphQLResponse,
25
25
  GraphQLTaggedNode,
26
26
  IEnvironment,
27
+ OperationAvailability,
27
28
  OperationType,
28
29
  Subscription,
29
30
  } from 'relay-runtime';
@@ -167,12 +168,17 @@ function preloadQueryDeduped<TQuery: OperationType>(
167
168
  }`;
168
169
  const prevQueryEntry = pendingQueries.get(cacheKey);
169
170
 
170
- const availability =
171
- fetchPolicy === STORE_OR_NETWORK_DEFAULT && query != null && query != null
171
+ function checkOperation(): OperationAvailability {
172
+ return query != null
172
173
  ? environment.check(
173
174
  createOperationDescriptor(query, variables, networkCacheConfig),
174
175
  )
175
176
  : {status: 'missing'};
177
+ }
178
+ const availability =
179
+ fetchPolicy === STORE_OR_NETWORK_DEFAULT
180
+ ? checkOperation()
181
+ : {status: 'missing'};
176
182
 
177
183
  let nextQueryEntry: ?PendingQueryEntry;
178
184
  if (availability.status === 'available' && query != null) {
@@ -203,7 +209,16 @@ function preloadQueryDeduped<TQuery: OperationType>(
203
209
  }
204
210
  } else if (prevQueryEntry == null || prevQueryEntry.kind !== 'network') {
205
211
  // Should fetch but we're not already fetching: fetch!
206
- const source = network.execute(params, variables, networkCacheConfig, null);
212
+ const source = network.execute(
213
+ params,
214
+ variables,
215
+ networkCacheConfig,
216
+ null,
217
+ undefined,
218
+ undefined,
219
+ undefined,
220
+ checkOperation,
221
+ );
207
222
  const subject = new ReplaySubject<GraphQLResponse>();
208
223
  nextQueryEntry = {
209
224
  cacheKey,
@@ -83,13 +83,10 @@ function handlePotentialSnapshotErrorsForState(
83
83
  state: FragmentState,
84
84
  ): void {
85
85
  if (state.kind === 'singular') {
86
- handlePotentialSnapshotErrors(
87
- environment,
88
- state.snapshot.errorResponseFields,
89
- );
86
+ handlePotentialSnapshotErrors(environment, state.snapshot.fieldErrors);
90
87
  } else if (state.kind === 'plural') {
91
88
  for (const snapshot of state.snapshots) {
92
- handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
89
+ handlePotentialSnapshotErrors(environment, snapshot.fieldErrors);
93
90
  }
94
91
  }
95
92
  }
@@ -48,7 +48,9 @@ type UseEntryPointLoaderHookReturnType<
48
48
  type NullEntryPointReference = {
49
49
  kind: 'NullEntryPointReference',
50
50
  };
51
- const initialNullEntryPointReferenceState = {kind: 'NullEntryPointReference'};
51
+ const initialNullEntryPointReferenceState: NullEntryPointReference = {
52
+ kind: 'NullEntryPointReference',
53
+ };
52
54
 
53
55
  hook useLoadEntryPoint<
54
56
  TEntryPointParams: {...},
@@ -122,7 +124,7 @@ hook useLoadEntryPoint<
122
124
 
123
125
  const disposeEntryPoint = useCallback(() => {
124
126
  if (isMountedRef.current) {
125
- const nullEntryPointReference = {
127
+ const nullEntryPointReference: NullEntryPointReference = {
126
128
  kind: 'NullEntryPointReference',
127
129
  };
128
130
  undisposedEntryPointReferencesRef.current.add(nullEntryPointReference);
@@ -54,6 +54,7 @@ hook useFragment(fragment: GraphQLTaggedNode, key: mixed): mixed {
54
54
  if (__DEV__) {
55
55
  // eslint-disable-next-line react-hooks/rules-of-hooks
56
56
  // $FlowFixMe[react-rule-hook]
57
+ // $FlowFixMe[react-rule-hook-conditional]
57
58
  useDebugValue({fragment: fragmentNode.name, data});
58
59
  }
59
60
  return data;
@@ -26,6 +26,7 @@ hook useFragmentInternal(
26
26
  ): ?SelectorData | Array<?SelectorData> {
27
27
  if (RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY) {
28
28
  // $FlowFixMe[react-rule-hook] - the condition is static
29
+ // $FlowFixMe[react-rule-hook-conditional]
29
30
  return useFragmentInternal_EXPERIMENTAL(
30
31
  fragmentNode,
31
32
  fragmentRef,
@@ -34,6 +35,7 @@ hook useFragmentInternal(
34
35
  );
35
36
  }
36
37
  // $FlowFixMe[react-rule-hook] - the condition is static
38
+ // $FlowFixMe[react-rule-hook-conditional]
37
39
  return useFragmentInternal_CURRENT(
38
40
  fragmentNode,
39
41
  fragmentRef,
@@ -14,6 +14,7 @@
14
14
  import type {QueryResult} from './QueryResource';
15
15
  import type {
16
16
  CacheConfig,
17
+ DataID,
17
18
  FetchPolicy,
18
19
  IEnvironment,
19
20
  ReaderFragment,
@@ -21,13 +22,11 @@ import type {
21
22
  SelectorData,
22
23
  Snapshot,
23
24
  } from 'relay-runtime';
24
- import type {
25
- MissingClientEdgeRequestInfo,
26
- MissingLiveResolverField,
27
- } from 'relay-runtime/store/RelayStoreTypes';
25
+ import type {MissingClientEdgeRequestInfo} from 'relay-runtime/store/RelayStoreTypes';
28
26
 
29
27
  const {getQueryResourceForEnvironment} = require('./QueryResource');
30
28
  const useRelayEnvironment = require('./useRelayEnvironment');
29
+ const useRelayLoggingContext = require('./useRelayLoggingContext');
31
30
  const invariant = require('invariant');
32
31
  const {useDebugValue, useEffect, useMemo, useRef, useState} = require('react');
33
32
  const {
@@ -89,13 +88,13 @@ function getMissingClientEdges(
89
88
 
90
89
  function getSuspendingLiveResolver(
91
90
  state: FragmentState,
92
- ): $ReadOnlyArray<MissingLiveResolverField> | null {
91
+ ): $ReadOnlyArray<DataID> | null {
93
92
  if (state.kind === 'bailout') {
94
93
  return null;
95
94
  } else if (state.kind === 'singular') {
96
95
  return state.snapshot.missingLiveResolverFields ?? null;
97
96
  } else {
98
- let missingFields: null | Array<MissingLiveResolverField> = null;
97
+ let missingFields: null | Array<DataID> = null;
99
98
  for (const snapshot of state.snapshots) {
100
99
  if (snapshot.missingLiveResolverFields) {
101
100
  missingFields = missingFields ?? [];
@@ -111,15 +110,21 @@ function getSuspendingLiveResolver(
111
110
  function handlePotentialSnapshotErrorsForState(
112
111
  environment: IEnvironment,
113
112
  state: FragmentState,
113
+ loggingContext: mixed | void,
114
114
  ): void {
115
115
  if (state.kind === 'singular') {
116
116
  handlePotentialSnapshotErrors(
117
117
  environment,
118
- state.snapshot.errorResponseFields,
118
+ state.snapshot.fieldErrors,
119
+ loggingContext,
119
120
  );
120
121
  } else if (state.kind === 'plural') {
121
122
  for (const snapshot of state.snapshots) {
122
- handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
123
+ handlePotentialSnapshotErrors(
124
+ environment,
125
+ snapshot.fieldErrors,
126
+ loggingContext,
127
+ );
123
128
  }
124
129
  }
125
130
  }
@@ -155,7 +160,7 @@ function handleMissedUpdates(
155
160
  missingLiveResolverFields: currentSnapshot.missingLiveResolverFields,
156
161
  seenRecords: currentSnapshot.seenRecords,
157
162
  selector: currentSnapshot.selector,
158
- errorResponseFields: currentSnapshot.errorResponseFields,
163
+ fieldErrors: currentSnapshot.fieldErrors,
159
164
  };
160
165
  return [
161
166
  updatedData !== state.snapshot.data,
@@ -179,7 +184,7 @@ function handleMissedUpdates(
179
184
  missingLiveResolverFields: currentSnapshot.missingLiveResolverFields,
180
185
  seenRecords: currentSnapshot.seenRecords,
181
186
  selector: currentSnapshot.selector,
182
- errorResponseFields: currentSnapshot.errorResponseFields,
187
+ fieldErrors: currentSnapshot.fieldErrors,
183
188
  };
184
189
  if (updatedData !== snapshot.data) {
185
190
  didMissUpdates = true;
@@ -405,6 +410,12 @@ hook useFragmentInternal(
405
410
  );
406
411
 
407
412
  const environment = useRelayEnvironment();
413
+ let loggerContext;
414
+ if (RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER) {
415
+ // $FlowFixMe[react-rule-hook] - the condition is static
416
+ // $FlowFixMe[react-rule-hook-conditional]
417
+ loggerContext = useRelayLoggingContext();
418
+ }
408
419
  const [_state, setState] = useState<FragmentState>(() =>
409
420
  getFragmentState(environment, fragmentSelector),
410
421
  );
@@ -456,6 +467,7 @@ hook useFragmentInternal(
456
467
  // always or never run for a given invocation of this hook.
457
468
  // eslint-disable-next-line react-hooks/rules-of-hooks
458
469
  // $FlowFixMe[react-rule-hook]
470
+ // $FlowFixMe[react-rule-hook-conditional]
459
471
  const [clientEdgeQueries, activeRequestPromises] = useMemo(() => {
460
472
  const missingClientEdges = getMissingClientEdges(state);
461
473
  // eslint-disable-next-line no-shadow
@@ -487,6 +499,7 @@ hook useFragmentInternal(
487
499
  // See above note
488
500
  // eslint-disable-next-line react-hooks/rules-of-hooks
489
501
  // $FlowFixMe[react-rule-hook]
502
+ // $FlowFixMe[react-rule-hook-conditional]
490
503
  useEffect(() => {
491
504
  const QueryResource = getQueryResourceForEnvironment(environment);
492
505
  if (clientEdgeQueries?.length) {
@@ -508,7 +521,7 @@ hook useFragmentInternal(
508
521
  const suspendingLiveResolvers = getSuspendingLiveResolver(state);
509
522
  if (suspendingLiveResolvers != null && suspendingLiveResolvers.length > 0) {
510
523
  throw Promise.all(
511
- suspendingLiveResolvers.map(({liveStateID}) => {
524
+ suspendingLiveResolvers.map(liveStateID => {
512
525
  // $FlowFixMe[prop-missing] This is expected to be a RelayModernStore
513
526
  return environment.getStore().getLiveResolverPromise(liveStateID);
514
527
  }),
@@ -543,7 +556,7 @@ hook useFragmentInternal(
543
556
 
544
557
  // Report required fields only if we're not suspending, since that means
545
558
  // they're missing even though we are out of options for possibly fetching them:
546
- handlePotentialSnapshotErrorsForState(environment, state);
559
+ handlePotentialSnapshotErrorsForState(environment, state, loggerContext);
547
560
 
548
561
  const hasPendingStateChanges = useRef<boolean>(false);
549
562
 
@@ -596,6 +609,7 @@ hook useFragmentInternal(
596
609
  const fragmentRefIsNullish = fragmentRef == null; // for less sensitive memoization
597
610
  // eslint-disable-next-line react-hooks/rules-of-hooks
598
611
  // $FlowFixMe[react-rule-hook]
612
+ // $FlowFixMe[react-rule-hook-conditional]
599
613
  data = useMemo(() => {
600
614
  if (state.kind === 'bailout') {
601
615
  // Bailout state can happen if the fragmentRef is a plural array that is empty or has no
@@ -647,6 +661,7 @@ hook useFragmentInternal(
647
661
  if (__DEV__) {
648
662
  // eslint-disable-next-line react-hooks/rules-of-hooks
649
663
  // $FlowFixMe[react-rule-hook]
664
+ // $FlowFixMe[react-rule-hook-conditional]
650
665
  useDebugValue({fragment: fragmentNode.name, data});
651
666
  }
652
667