react-relay 16.1.0 → 16.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. package/ReactRelayContext.js +1 -1
  2. package/ReactRelayTypes.js.flow +1 -0
  3. package/hooks.js +1 -1
  4. package/hooks.js.flow +1 -1
  5. package/index.js +1 -1
  6. package/index.js.flow +1 -1
  7. package/legacy.js +1 -1
  8. package/lib/relay-hooks/SuspenseResource.js +7 -4
  9. package/lib/relay-hooks/experimental/readFragmentInternal_EXPERIMENTAL.js +2 -2
  10. package/lib/relay-hooks/experimental/useFragmentInternal_EXPERIMENTAL.js +61 -27
  11. package/lib/relay-hooks/{FragmentResource.js → legacy/FragmentResource.js} +7 -6
  12. package/lib/relay-hooks/{useBlockingPaginationFragment.js → legacy/useBlockingPaginationFragment.js} +2 -2
  13. package/lib/relay-hooks/{useFragmentNode.js → legacy/useFragmentNode.js} +2 -2
  14. package/lib/relay-hooks/{useRefetchableFragmentNode.js → legacy/useRefetchableFragmentNode.js} +8 -8
  15. package/lib/relay-hooks/useFragment.js +1 -1
  16. package/lib/relay-hooks/useLazyLoadQueryNode.js +1 -1
  17. package/lib/relay-hooks/usePaginationFragment.js +1 -1
  18. package/lib/relay-hooks/useRefetchableFragment.js +1 -1
  19. package/package.json +2 -2
  20. package/react-relay-hooks.js +2 -2
  21. package/react-relay-hooks.min.js +2 -2
  22. package/react-relay-legacy.js +1 -1
  23. package/react-relay-legacy.min.js +1 -1
  24. package/react-relay.js +2 -2
  25. package/react-relay.min.js +2 -2
  26. package/relay-hooks/EntryPointTypes.flow.js.flow +8 -14
  27. package/relay-hooks/NestedRelayEntryPointBuilderUtils.js.flow +5 -11
  28. package/relay-hooks/SuspenseResource.js.flow +11 -8
  29. package/relay-hooks/__flowtests__/useBlockingPaginationFragment-flowtest.js.flow +1 -1
  30. package/relay-hooks/experimental/readFragmentInternal_EXPERIMENTAL.js.flow +2 -0
  31. package/relay-hooks/experimental/useFragmentInternal_EXPERIMENTAL.js.flow +57 -14
  32. package/relay-hooks/experimental/useFragment_EXPERIMENTAL.js.flow +9 -3
  33. package/relay-hooks/{FragmentResource.js.flow → legacy/FragmentResource.js.flow} +8 -5
  34. package/relay-hooks/{useBlockingPaginationFragment.js.flow → legacy/useBlockingPaginationFragment.js.flow} +4 -4
  35. package/relay-hooks/{useFragmentNode.js.flow → legacy/useFragmentNode.js.flow} +2 -2
  36. package/relay-hooks/{useRefetchableFragmentNode.js.flow → legacy/useRefetchableFragmentNode.js.flow} +7 -7
  37. package/relay-hooks/loadQuery.js.flow +1 -1
  38. package/relay-hooks/useFragment.js.flow +10 -4
  39. package/relay-hooks/useLazyLoadQueryNode.js.flow +1 -1
  40. package/relay-hooks/usePaginationFragment.js.flow +2 -2
  41. package/relay-hooks/useRefetchableFragment.js.flow +2 -2
@@ -31,7 +31,7 @@ const useRelayEnvironment = require('../useRelayEnvironment');
31
31
  const invariant = require('invariant');
32
32
  const {useDebugValue, useEffect, useMemo, useRef, useState} = require('react');
33
33
  const {
34
- __internal: {fetchQuery: fetchQueryInternal},
34
+ __internal: {fetchQuery: fetchQueryInternal, getPromiseForActiveRequest},
35
35
  RelayFeatureFlags,
36
36
  areEqualSelectors,
37
37
  createOperationDescriptor,
@@ -117,6 +117,7 @@ function handlePotentialSnapshotErrorsForState(
117
117
  environment,
118
118
  state.snapshot.missingRequiredFields,
119
119
  state.snapshot.relayResolverErrors,
120
+ state.snapshot.errorResponseFields,
120
121
  );
121
122
  } else if (state.kind === 'plural') {
122
123
  for (const snapshot of state.snapshots) {
@@ -124,6 +125,7 @@ function handlePotentialSnapshotErrorsForState(
124
125
  environment,
125
126
  snapshot.missingRequiredFields,
126
127
  snapshot.relayResolverErrors,
128
+ snapshot.errorResponseFields,
127
129
  );
128
130
  }
129
131
  }
@@ -162,6 +164,7 @@ function handleMissedUpdates(
162
164
  selector: currentSnapshot.selector,
163
165
  missingRequiredFields: currentSnapshot.missingRequiredFields,
164
166
  relayResolverErrors: currentSnapshot.relayResolverErrors,
167
+ errorResponseFields: currentSnapshot.errorResponseFields,
165
168
  };
166
169
  return [
167
170
  updatedData !== state.snapshot.data,
@@ -187,6 +190,7 @@ function handleMissedUpdates(
187
190
  selector: currentSnapshot.selector,
188
191
  missingRequiredFields: currentSnapshot.missingRequiredFields,
189
192
  relayResolverErrors: currentSnapshot.relayResolverErrors,
193
+ errorResponseFields: currentSnapshot.errorResponseFields,
190
194
  };
191
195
  if (updatedData !== snapshot.data) {
192
196
  didMissUpdates = true;
@@ -214,7 +218,7 @@ function handleMissingClientEdge(
214
218
  parentFragmentRef: mixed,
215
219
  missingClientEdgeRequestInfo: MissingClientEdgeRequestInfo,
216
220
  queryOptions?: FragmentQueryOptions,
217
- ): QueryResult {
221
+ ): [QueryResult, ?Promise<mixed>] {
218
222
  const originalVariables = getVariablesFromFragment(
219
223
  parentFragmentNode,
220
224
  parentFragmentRef,
@@ -233,17 +237,23 @@ function handleMissingClientEdge(
233
237
  // according to the component mount/suspense cycle; QueryResource
234
238
  // already handles this by itself.
235
239
  const QueryResource = getQueryResourceForEnvironment(environment);
236
- return QueryResource.prepare(
240
+ const queryResult = QueryResource.prepare(
237
241
  queryOperationDescriptor,
238
242
  fetchQueryInternal(environment, queryOperationDescriptor),
239
243
  queryOptions?.fetchPolicy,
240
244
  );
245
+
246
+ return [
247
+ queryResult,
248
+ getPromiseForActiveRequest(environment, queryOperationDescriptor.request),
249
+ ];
241
250
  }
242
251
 
243
252
  function subscribeToSnapshot(
244
253
  environment: IEnvironment,
245
254
  state: FragmentState,
246
255
  setState: StateUpdaterFunction<FragmentState>,
256
+ hasPendingStateChanges: {current: boolean},
247
257
  ): () => void {
248
258
  if (state.kind === 'bailout') {
249
259
  return () => {};
@@ -264,11 +274,14 @@ function subscribeToSnapshot(
264
274
  name: 'useFragment.subscription.missedUpdates',
265
275
  hasDataChanges: dataChanged,
266
276
  });
277
+ hasPendingStateChanges.current = dataChanged;
267
278
  return dataChanged ? nextState : prevState;
268
279
  } else {
269
280
  return prevState;
270
281
  }
271
282
  }
283
+
284
+ hasPendingStateChanges.current = true;
272
285
  return {
273
286
  kind: 'singular',
274
287
  snapshot: latestSnapshot,
@@ -297,6 +310,8 @@ function subscribeToSnapshot(
297
310
  name: 'useFragment.subscription.missedUpdates',
298
311
  hasDataChanges: dataChanged,
299
312
  });
313
+ hasPendingStateChanges.current =
314
+ hasPendingStateChanges.current || dataChanged;
300
315
  return dataChanged ? nextState : prevState;
301
316
  } else {
302
317
  return prevState;
@@ -304,6 +319,7 @@ function subscribeToSnapshot(
304
319
  }
305
320
  const updated = [...prevState.snapshots];
306
321
  updated[index] = latestSnapshot;
322
+ hasPendingStateChanges.current = true;
307
323
  return {
308
324
  kind: 'plural',
309
325
  snapshots: updated,
@@ -450,27 +466,34 @@ function useFragmentInternal_EXPERIMENTAL(
450
466
  // a static (constant) property of the fragment. In practice, this effect will
451
467
  // always or never run for a given invocation of this hook.
452
468
  // eslint-disable-next-line react-hooks/rules-of-hooks
453
- const clientEdgeQueries = useMemo(() => {
469
+ const [clientEdgeQueries, activeRequestPromises] = useMemo(() => {
454
470
  const missingClientEdges = getMissingClientEdges(state);
455
471
  // eslint-disable-next-line no-shadow
456
472
  let clientEdgeQueries;
473
+ const activeRequestPromises = [];
457
474
  if (missingClientEdges?.length) {
458
475
  clientEdgeQueries = ([]: Array<QueryResult>);
459
476
  for (const edge of missingClientEdges) {
460
- clientEdgeQueries.push(
461
- handleMissingClientEdge(
462
- environment,
463
- fragmentNode,
464
- fragmentRef,
465
- edge,
466
- queryOptions,
467
- ),
477
+ const [queryResult, requestPromise] = handleMissingClientEdge(
478
+ environment,
479
+ fragmentNode,
480
+ fragmentRef,
481
+ edge,
482
+ queryOptions,
468
483
  );
484
+ clientEdgeQueries.push(queryResult);
485
+ if (requestPromise != null) {
486
+ activeRequestPromises.push(requestPromise);
487
+ }
469
488
  }
470
489
  }
471
- return clientEdgeQueries;
490
+ return [clientEdgeQueries, activeRequestPromises];
472
491
  }, [state, environment, fragmentNode, fragmentRef, queryOptions]);
473
492
 
493
+ if (activeRequestPromises.length) {
494
+ throw Promise.all(activeRequestPromises);
495
+ }
496
+
474
497
  // See above note
475
498
  // eslint-disable-next-line react-hooks/rules-of-hooks
476
499
  useEffect(() => {
@@ -505,6 +528,7 @@ function useFragmentInternal_EXPERIMENTAL(
505
528
  // We only suspend when the component is first trying to mount or changing
506
529
  // selectors, not if data becomes missing later:
507
530
  if (
531
+ environment !== previousEnvironment ||
508
532
  !committedFragmentSelectorRef.current ||
509
533
  !areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector)
510
534
  ) {
@@ -528,6 +552,8 @@ function useFragmentInternal_EXPERIMENTAL(
528
552
  // they're missing even though we are out of options for possibly fetching them:
529
553
  handlePotentialSnapshotErrorsForState(environment, state);
530
554
 
555
+ const hasPendingStateChanges = useRef<boolean>(false);
556
+
531
557
  useEffect(() => {
532
558
  // Check for updates since the state was rendered
533
559
  let currentState = subscribedState;
@@ -546,9 +572,26 @@ function useFragmentInternal_EXPERIMENTAL(
546
572
  }
547
573
  currentState = updatedState;
548
574
  }
549
- return subscribeToSnapshot(environment, currentState, setState);
575
+ return subscribeToSnapshot(
576
+ environment,
577
+ currentState,
578
+ setState,
579
+ hasPendingStateChanges,
580
+ );
550
581
  }, [environment, subscribedState]);
551
582
 
583
+ if (hasPendingStateChanges.current) {
584
+ const updates = handleMissedUpdates(environment, state);
585
+ if (updates != null) {
586
+ const [hasStateUpdates, updatedState] = updates;
587
+ if (hasStateUpdates) {
588
+ setState(updatedState);
589
+ state = updatedState;
590
+ }
591
+ }
592
+ hasPendingStateChanges.current = false;
593
+ }
594
+
552
595
  let data: ?SelectorData | Array<?SelectorData>;
553
596
  if (isPlural) {
554
597
  // Plural fragments require allocating an array of the snapshot data values,
@@ -30,17 +30,23 @@ 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
+
33
39
  // if the key is a non-nullable array of keys, return non-nullable array
34
40
  declare function useFragment<TFragmentType: FragmentType, TData>(
35
41
  fragment: Fragment<TFragmentType, TData>,
36
42
  key: $ReadOnlyArray<HasSpread<TFragmentType>>,
37
43
  ): TData;
38
44
 
39
- // if the key is null/void, return null/void value
45
+ // if the key is a nullable array of keys, return nullable array
40
46
  declare function useFragment<TFragmentType: FragmentType, TData>(
41
47
  fragment: Fragment<TFragmentType, TData>,
42
- key: null | void,
43
- ): null | void;
48
+ key: ?$ReadOnlyArray<HasSpread<TFragmentType>>,
49
+ ): ?TData;
44
50
 
45
51
  function useFragment(fragment: GraphQLTaggedNode, key: mixed): mixed {
46
52
  // We need to use this hook in order to be able to track if
@@ -11,8 +11,8 @@
11
11
 
12
12
  'use strict';
13
13
 
14
- import type {Cache} from './LRUCache';
15
- import type {QueryResource, QueryResult} from './QueryResource';
14
+ import type {Cache} from '../LRUCache';
15
+ import type {QueryResource, QueryResult} from '../QueryResource';
16
16
  import type {
17
17
  ConcreteRequest,
18
18
  DataID,
@@ -24,9 +24,9 @@ import type {
24
24
  } from 'relay-runtime';
25
25
  import type {MissingLiveResolverField} from 'relay-runtime/store/RelayStoreTypes';
26
26
 
27
- const LRUCache = require('./LRUCache');
28
- const {getQueryResourceForEnvironment} = require('./QueryResource');
29
- const SuspenseResource = require('./SuspenseResource');
27
+ const LRUCache = require('../LRUCache');
28
+ const {getQueryResourceForEnvironment} = require('../QueryResource');
29
+ const SuspenseResource = require('../SuspenseResource');
30
30
  const invariant = require('invariant');
31
31
  const {
32
32
  __internal: {fetchQuery, getPromiseForActiveRequest},
@@ -566,6 +566,7 @@ class FragmentResourceImpl {
566
566
  this._environment,
567
567
  s.missingRequiredFields,
568
568
  s.relayResolverErrors,
569
+ s.errorResponseFields,
569
570
  );
570
571
  });
571
572
  } else {
@@ -573,6 +574,7 @@ class FragmentResourceImpl {
573
574
  this._environment,
574
575
  snapshot.missingRequiredFields,
575
576
  snapshot.relayResolverErrors,
577
+ snapshot.errorResponseFields,
576
578
  );
577
579
  }
578
580
  }
@@ -776,6 +778,7 @@ class FragmentResourceImpl {
776
778
  selector: currentSnapshot.selector,
777
779
  missingRequiredFields: currentSnapshot.missingRequiredFields,
778
780
  relayResolverErrors: currentSnapshot.relayResolverErrors,
781
+ errorResponseFields: currentSnapshot.errorResponseFields,
779
782
  };
780
783
  if (updatedData !== renderData) {
781
784
  const result = getFragmentResult(
@@ -11,9 +11,9 @@
11
11
 
12
12
  'use strict';
13
13
 
14
- import type {RefetchableFragment} from '../../relay-runtime/util/RelayRuntimeTypes';
15
- import type {LoadMoreFn, UseLoadMoreFunctionArgs} from './useLoadMoreFunction';
14
+ import type {LoadMoreFn, UseLoadMoreFunctionArgs} from '../useLoadMoreFunction';
16
15
  import type {Options} from './useRefetchableFragmentNode';
16
+ import type {RefetchableFragment} from 'relay-runtime';
17
17
  import type {
18
18
  Disposable,
19
19
  FragmentType,
@@ -22,9 +22,9 @@ import type {
22
22
  Variables,
23
23
  } from 'relay-runtime';
24
24
 
25
- const useLoadMoreFunction = require('./useLoadMoreFunction');
25
+ const useLoadMoreFunction = require('../useLoadMoreFunction');
26
+ const useStaticFragmentNodeWarning = require('../useStaticFragmentNodeWarning');
26
27
  const useRefetchableFragmentNode = require('./useRefetchableFragmentNode');
27
- const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
28
28
  const invariant = require('invariant');
29
29
  const {useCallback, useEffect, useRef, useState} = require('react');
30
30
  const {
@@ -13,9 +13,9 @@
13
13
 
14
14
  import type {ReaderFragment} from 'relay-runtime';
15
15
 
16
+ const useRelayEnvironment = require('../useRelayEnvironment');
17
+ const useUnsafeRef_DEPRECATED = require('../useUnsafeRef_DEPRECATED');
16
18
  const {getFragmentResourceForEnvironment} = require('./FragmentResource');
17
- const useRelayEnvironment = require('./useRelayEnvironment');
18
- const useUnsafeRef_DEPRECATED = require('./useUnsafeRef_DEPRECATED');
19
19
  const {useEffect, useState} = require('react');
20
20
  const {RelayFeatureFlags, getFragmentIdentifier} = require('relay-runtime');
21
21
  const warning = require('warning');
@@ -11,8 +11,8 @@
11
11
 
12
12
  'use strict';
13
13
 
14
- import type {RefetchableIdentifierInfo} from '../../relay-runtime/util/ReaderNode';
15
- import type {LoaderFn} from './useQueryLoader';
14
+ import type {LoaderFn} from '../useQueryLoader';
15
+ import type {RefetchableIdentifierInfo} from 'relay-runtime';
16
16
  import type {
17
17
  ConcreteRequest,
18
18
  Disposable,
@@ -26,13 +26,13 @@ import type {
26
26
  VariablesOf,
27
27
  } from 'relay-runtime';
28
28
 
29
+ const ProfilerContext = require('../ProfilerContext');
30
+ const {getQueryResourceForEnvironment} = require('../QueryResource');
31
+ const useIsMountedRef = require('../useIsMountedRef');
32
+ const useQueryLoader = require('../useQueryLoader');
33
+ const useRelayEnvironment = require('../useRelayEnvironment');
29
34
  const {getFragmentResourceForEnvironment} = require('./FragmentResource');
30
- const ProfilerContext = require('./ProfilerContext');
31
- const {getQueryResourceForEnvironment} = require('./QueryResource');
32
35
  const useFragmentNode = require('./useFragmentNode');
33
- const useIsMountedRef = require('./useIsMountedRef');
34
- const useQueryLoader = require('./useQueryLoader');
35
- const useRelayEnvironment = require('./useRelayEnvironment');
36
36
  const invariant = require('invariant');
37
37
  const {useCallback, useContext, useReducer} = require('react');
38
38
  const {
@@ -62,7 +62,7 @@ type QueryType<T> = T extends Query<infer V, infer D, infer RR>
62
62
  variables: V,
63
63
  response: D,
64
64
  rawResponse?: $NonMaybeType<RR>,
65
- }
65
+ } // $FlowFixMe[deprecated-type]
66
66
  : $Call<<T>(PreloadableConcreteRequest<T>) => T, T>;
67
67
 
68
68
  declare function loadQuery<
@@ -14,8 +14,8 @@
14
14
  import type {Fragment, FragmentType, GraphQLTaggedNode} from 'relay-runtime';
15
15
 
16
16
  const HooksImplementation = require('./HooksImplementation');
17
+ const useFragmentNode = require('./legacy/useFragmentNode');
17
18
  const {useTrackLoadQueryInRender} = require('./loadQuery');
18
- const useFragmentNode = require('./useFragmentNode');
19
19
  const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
20
20
  const {useDebugValue} = require('react');
21
21
  const {getFragment} = require('relay-runtime');
@@ -31,17 +31,23 @@ declare function useFragment<TFragmentType: FragmentType, TData>(
31
31
  key: HasSpread<TFragmentType>,
32
32
  ): TData;
33
33
 
34
+ // if the key is nullable, return nullable value
35
+ declare function useFragment<TFragmentType: FragmentType, TData>(
36
+ fragment: Fragment<TFragmentType, TData>,
37
+ key: ?HasSpread<TFragmentType>,
38
+ ): ?TData;
39
+
34
40
  // if the key is a non-nullable array of keys, return non-nullable array
35
41
  declare function useFragment<TFragmentType: FragmentType, TData>(
36
42
  fragment: Fragment<TFragmentType, TData>,
37
43
  key: $ReadOnlyArray<HasSpread<TFragmentType>>,
38
44
  ): TData;
39
45
 
40
- // if the key is null/void, return null/void value
46
+ // if the key is a nullable array of keys, return nullable array
41
47
  declare function useFragment<TFragmentType: FragmentType, TData>(
42
48
  fragment: Fragment<TFragmentType, TData>,
43
- key: null | void,
44
- ): null | void;
49
+ key: ?$ReadOnlyArray<HasSpread<TFragmentType>>,
50
+ ): ?TData;
45
51
 
46
52
  function useFragment_LEGACY(fragment: GraphQLTaggedNode, key: mixed): mixed {
47
53
  // We need to use this hook in order to be able to track if
@@ -22,13 +22,13 @@ import type {
22
22
  import type {ReaderFragment} from 'relay-runtime/util/ReaderNode';
23
23
 
24
24
  const HooksImplementation = require('./HooksImplementation');
25
+ const useFragmentNode = require('./legacy/useFragmentNode');
25
26
  const ProfilerContext = require('./ProfilerContext');
26
27
  const {
27
28
  getQueryCacheIdentifier,
28
29
  getQueryResourceForEnvironment,
29
30
  } = require('./QueryResource');
30
31
  const useFetchTrackingRef = require('./useFetchTrackingRef');
31
- const useFragmentNode = require('./useFragmentNode');
32
32
  const useRelayEnvironment = require('./useRelayEnvironment');
33
33
  const React = require('react');
34
34
 
@@ -11,9 +11,9 @@
11
11
 
12
12
  'use strict';
13
13
 
14
+ import type {Options} from './legacy/useRefetchableFragmentNode';
14
15
  import type {LoadMoreFn, UseLoadMoreFunctionArgs} from './useLoadMoreFunction';
15
16
  import type {RefetchFn} from './useRefetchableFragment';
16
- import type {Options} from './useRefetchableFragmentNode';
17
17
  import type {
18
18
  FragmentType,
19
19
  GraphQLResponse,
@@ -23,8 +23,8 @@ import type {
23
23
  } from 'relay-runtime';
24
24
 
25
25
  const HooksImplementation = require('./HooksImplementation');
26
+ const useRefetchableFragmentNode = require('./legacy/useRefetchableFragmentNode');
26
27
  const useLoadMoreFunction = require('./useLoadMoreFunction');
27
- const useRefetchableFragmentNode = require('./useRefetchableFragmentNode');
28
28
  const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
29
29
  const {useCallback, useDebugValue, useState} = require('react');
30
30
  const {
@@ -11,7 +11,7 @@
11
11
 
12
12
  'use strict';
13
13
 
14
- import type {Options} from './useRefetchableFragmentNode';
14
+ import type {Options} from './legacy/useRefetchableFragmentNode';
15
15
  import type {
16
16
  Disposable,
17
17
  FragmentType,
@@ -20,7 +20,7 @@ import type {
20
20
  } from 'relay-runtime';
21
21
 
22
22
  const HooksImplementation = require('./HooksImplementation');
23
- const useRefetchableFragmentNode = require('./useRefetchableFragmentNode');
23
+ const useRefetchableFragmentNode = require('./legacy/useRefetchableFragmentNode');
24
24
  const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning');
25
25
  const {useDebugValue} = require('react');
26
26
  const {getFragment} = require('relay-runtime');